Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 49 additions & 4 deletions ci/phpunit/TestBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,23 @@
use Hashtopolis\dba\models\CrackerBinary;
use Hashtopolis\dba\models\CrackerBinaryType;
use Hashtopolis\dba\models\File;
use Hashtopolis\dba\models\FileDownload;
use Hashtopolis\dba\models\FileTask;
use Hashtopolis\dba\models\Hashlist;
use Hashtopolis\dba\models\HashType;
use Hashtopolis\dba\models\HealthCheck;
use Hashtopolis\dba\models\HealthCheckAgent;
use Hashtopolis\dba\models\JwtApiKey;
use Hashtopolis\dba\models\RightGroup;
use Hashtopolis\dba\models\Task;
use Hashtopolis\dba\models\TaskWrapper;
use Hashtopolis\dba\models\User;
use Hashtopolis\dba\models\UserFactory;
use Hashtopolis\inc\defines\DHealthCheckAgentStatus;
use Hashtopolis\inc\defines\DHealthCheckMode;
use Hashtopolis\inc\defines\DHealthCheckStatus;
use Hashtopolis\inc\defines\DHealthCheckType;
use Hashtopolis\inc\defines\DFileDownloadStatus;
use Hashtopolis\inc\defines\DHashlistFormat;
use Hashtopolis\inc\defines\DTaskTypes;
use Hashtopolis\inc\utils\UserUtils;
Expand Down Expand Up @@ -157,19 +166,46 @@ protected function createCrackerBinary(CrackerBinaryType $crackerBinaryType): Cr
return $crackerBinary;
}

protected function createTask(TaskWrapper $taskWrapper, CrackerBinary $crackerBinary, CrackerBinaryType $crackerBinaryType): Task {
protected function createTask(TaskWrapper $taskWrapper, CrackerBinary $crackerBinary, CrackerBinaryType $crackerBinaryType, ?int $usePreprocessor = null, string $preprocessorCommand = ''): Task {
$task = $this->createDatabaseObject(
Factory::getTaskFactory(),
new Task(null, 'task_' . uniqid(), '--attack-mode 0', 60, 30, 0, 0, 1, 1, '#ffffff', 0, 0, 0, 0, $crackerBinary->getId(), $crackerBinaryType->getId(), $taskWrapper->getId(), 0, '', 0, 0, 0, 0, '')
new Task(null, 'task_' . uniqid(), '--attack-mode 0', 60, 30, 0, 0, 1, 1, '#ffffff', 0, 0, 0, 0, $crackerBinary->getId(), $crackerBinaryType->getId(), $taskWrapper->getId(), 0, '', 0, 0, 0, $usePreprocessor ?? 0, $preprocessorCommand)
);
$this->assertTrue($task instanceof Task);
return $task;
}

protected function createFile(AccessGroup $group, int $isSecret = 0): File {
protected function createJwtApiKey(User $user, ?int $startValid = null, ?int $endValid = null, int $isRevoked = 0): JwtApiKey {
$key = $this->createDatabaseObject(
Factory::getJwtApiKeyFactory(),
new JwtApiKey(null, $startValid ?? time(), $endValid ?? time() + 3600, $user->getId(), $isRevoked)
);
$this->assertTrue($key instanceof JwtApiKey);
return $key;
}

protected function createHealthCheck(CrackerBinary $crackerBinary, int $status = DHealthCheckStatus::PENDING, int $checkType = DHealthCheckType::BRUTE_FORCE, int $hashtypeId = DHealthCheckMode::MD5, int $expectedCracks = 0, string $attackCmd = ''): HealthCheck {
$check = $this->createDatabaseObject(
Factory::getHealthCheckFactory(),
new HealthCheck(null, time(), $status, $checkType, $hashtypeId, $crackerBinary->getId(), $expectedCracks, $attackCmd)
);
$this->assertTrue($check instanceof HealthCheck);
return $check;
}

protected function createHealthCheckAgent(HealthCheck $healthCheck, Agent $agent, int $status = DHealthCheckAgentStatus::PENDING, int $cracked = 0, int $numGpus = 0, int $start = 0, int $end = 0, string $errors = ''): HealthCheckAgent {
$agentCheck = $this->createDatabaseObject(
Factory::getHealthCheckAgentFactory(),
new HealthCheckAgent(null, $healthCheck->getId(), $agent->getId(), $status, $cracked, $numGpus, $start, $end, $errors)
);
$this->assertTrue($agentCheck instanceof HealthCheckAgent);
return $agentCheck;
}

protected function createFile(AccessGroup $group, int $isSecret = 0, ?string $filename = null, int $size = 0, int $fileType = 0, int $lineCount = 0): File {
$file = $this->createDatabaseObject(
Factory::getFileFactory(),
new File(null, 'file_' . uniqid(), 0, $isSecret, 0, $group->getId(), 0)
new File(null, $filename ?? 'file_' . uniqid(), $size, $isSecret, $fileType, $group->getId(), $lineCount)
);
$this->assertTrue($file instanceof File);
return $file;
Expand All @@ -183,6 +219,15 @@ protected function createFileTask(File $file, Task $task): FileTask {
$this->assertTrue($fileTask instanceof FileTask);
return $fileTask;
}

protected function createFileDownload(int $fileId, int $status = DFileDownloadStatus::PENDING): FileDownload {
$fileDownload = $this->createDatabaseObject(
Factory::getFileDownloadFactory(),
new FileDownload(null, time(), $fileId, $status)
);
$this->assertTrue($fileDownload instanceof FileDownload);
return $fileDownload;
}

protected function createAgent(string $prefix, int $isTrusted = 1): Agent {
$suffix = uniqid();
Expand Down
79 changes: 79 additions & 0 deletions ci/phpunit/inc/utils/FileDownloadUtilsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace Hashtopolis\inc\utils;

use Hashtopolis\dba\Factory;
use Hashtopolis\dba\models\File;
use Hashtopolis\dba\models\FileDownload;
use Hashtopolis\dba\QueryFilter;
use Hashtopolis\inc\defines\DFileDownloadStatus;
use Hashtopolis\TestBase;
use Override;

require_once(dirname(__FILE__) . '/../../TestBase.php');
require_once(dirname(__FILE__) . '/../../../../src/inc/startup/include.php');

final class FileDownloadUtilsTest extends TestBase {
private FileDownload $fileDownload;
private File $file;

#[Override]
protected function setUp(): void {
parent::setUp();
$group = $this->createAccessGroup('fdl_group');
$this->file = $this->createFile($group);
$this->fileDownload = $this->createFileDownload($this->file->getId(), DFileDownloadStatus::DONE);
}

public function testAddDownloadCreatesPendingDownload(): void {
$group = $this->createAccessGroup('fdl_new');
$newFile = $this->createFile($group);

FileDownloadUtils::addDownload($newFile->getId());

$qF1 = new QueryFilter(FileDownload::FILE_ID, $newFile->getId(), '=');
$qF2 = new QueryFilter(FileDownload::STATUS, DFileDownloadStatus::PENDING, '=');
$result = Factory::getFileDownloadFactory()->filter([Factory::FILTER => [$qF1, $qF2]], true);

$this->assertInstanceOf(FileDownload::class, $result);
$this->assertSame($newFile->getId(), $result->getFileId());
$this->assertSame(DFileDownloadStatus::PENDING, $result->getStatus());
$this->registerDatabaseObject(Factory::getFileDownloadFactory(), $result);
}

public function testAddDownloadSkipsExistingPending(): void {
$this->createFileDownload($this->file->getId());

FileDownloadUtils::addDownload($this->file->getId());

$qF1 = new QueryFilter(FileDownload::FILE_ID, $this->file->getId(), '=');
$qF2 = new QueryFilter(FileDownload::STATUS, DFileDownloadStatus::PENDING, '=');
$pending = Factory::getFileDownloadFactory()->filter([Factory::FILTER => [$qF1, $qF2]]);
$this->assertCount(1, $pending);
}

public function testAddDownloadCreatesNewForCompletedFile(): void {
FileDownloadUtils::addDownload($this->file->getId());

$qF1 = new QueryFilter(FileDownload::FILE_ID, $this->file->getId(), '=');
$qF2 = new QueryFilter(FileDownload::STATUS, DFileDownloadStatus::PENDING, '=');
$pending = Factory::getFileDownloadFactory()->filter([Factory::FILTER => [$qF1, $qF2]], true);

$this->assertInstanceOf(FileDownload::class, $pending);
$this->assertSame($this->file->getId(), $pending->getFileId());
$this->assertSame(DFileDownloadStatus::PENDING, $pending->getStatus());
$this->registerDatabaseObject(Factory::getFileDownloadFactory(), $pending);
}

public function testRemoveFileDeletesDownloads(): void {
FileDownloadUtils::removeFile($this->fileDownload->getFileId());

$qF = new QueryFilter(FileDownload::FILE_ID, $this->fileDownload->getFileId(), '=');
$remaining = Factory::getFileDownloadFactory()->filter([Factory::FILTER => $qF]);
$this->assertSame([], $remaining);
}

public function testRemoveFileIsNoopForNonExistent(): void {
FileDownloadUtils::removeFile(-1);
}
}
146 changes: 146 additions & 0 deletions ci/phpunit/inc/utils/FileUtilsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<?php

namespace Hashtopolis\inc\utils;

use Hashtopolis\dba\Factory;
use Hashtopolis\dba\models\AccessGroup;
use Hashtopolis\dba\models\File;
use Hashtopolis\dba\models\User;
use Hashtopolis\inc\defines\DFileType;
use Hashtopolis\inc\HTException;
use Hashtopolis\TestBase;
use Override;

require_once(dirname(__FILE__) . '/../../TestBase.php');
require_once(dirname(__FILE__) . '/../../../../src/inc/startup/include.php');

final class FileUtilsTest extends TestBase {
private User $user;
private AccessGroup $group;
private File $file;
private File $ruleFile;
private File $wordlistFile;
private File $otherFile;

#[Override]
protected function setUp(): void {
parent::setUp();

$this->user = $this->createUser('fu_user');
$this->group = $this->createAccessGroup('fu_group');
$this->createAccessGroupUser($this->user, $this->group);

$this->file = $this->createFile($this->group);
$this->ruleFile = $this->createFile($this->group, 0, 'test_rule_' . uniqid() . '.rule', 512, DFileType::RULE);
$this->wordlistFile = $this->createFile($this->group);
$this->otherFile = $this->createFile($this->group, 0, 'test_other_' . uniqid() . '.bin', 256, DFileType::OTHER);
}

public function testGetFileReturnsFileForAuthorizedUser(): void {
$result = FileUtils::getFile($this->file->getId(), $this->user);
$this->assertInstanceOf(File::class, $result);
$this->assertSame($this->file->getId(), $result->getId());
}

public function testGetFileThrowsForInvalidId(): void {
$this->expectException(HTException::class);
FileUtils::getFile(-1, $this->user);
}

public function testGetFileThrowsForUnauthorizedUser(): void {
$otherGroup = $this->createAccessGroup('fu_other');
$otherFile = $this->createFile($otherGroup);

$this->expectException(HTException::class);
FileUtils::getFile($otherFile->getId(), $this->user);
}

public function testSetFileTypeUpdatesType(): void {
FileUtils::setFileType($this->file->getId(), DFileType::RULE, $this->user);

$updated = Factory::getFileFactory()->get($this->file->getId());
$this->assertSame(DFileType::RULE, $updated->getFileType());
}

public function testSetFileTypeThrowsForInvalidType(): void {
$this->expectException(HTException::class);
FileUtils::setFileType($this->file->getId(), 999, $this->user);
}

public function testSwitchSecretTogglesSecret(): void {
FileUtils::switchSecret($this->file->getId(), 1, $this->user);

$updated = Factory::getFileFactory()->get($this->file->getId());
$this->assertSame(1, $updated->getIsSecret());

FileUtils::switchSecret($this->file->getId(), 0, $this->user);

$updated = Factory::getFileFactory()->get($this->file->getId());
$this->assertSame(0, $updated->getIsSecret());
}

public function testGetFilesReturnsFilesInUserAccessGroups(): void {
$files = FileUtils::getFiles($this->user);
$fileIds = array_map(fn(File $f) => $f->getId(), $files);

$this->assertContains($this->file->getId(), $fileIds);
$this->assertContains($this->ruleFile->getId(), $fileIds);
$this->assertContains($this->wordlistFile->getId(), $fileIds);
$this->assertContains($this->otherFile->getId(), $fileIds);
}

public function testGetFilesExcludesTemporaryFiles(): void {
$tempFile = $this->createFile($this->group, 0, 'temp_' . uniqid() . '.tmp', 0, DFileType::TEMPORARY);

$files = FileUtils::getFiles($this->user);
$fileIds = array_map(fn(File $f) => $f->getId(), $files);

$this->assertNotContains($tempFile->getId(), $fileIds);
}

public function testLoadFilesByCategoryCategorizesFiles(): void {
[$rules, $wordlists, $other] = FileUtils::loadFilesByCategory($this->user, []);

$ruleIds = array_map(fn($set) => $set->getAllValues()['file']->getId(), $rules);
$wlIds = array_map(fn($set) => $set->getAllValues()['file']->getId(), $wordlists);
$otherIds = array_map(fn($set) => $set->getAllValues()['file']->getId(), $other);

$this->assertContains($this->ruleFile->getId(), $ruleIds);
$this->assertContains($this->file->getId(), $wlIds);
$this->assertContains($this->wordlistFile->getId(), $wlIds);
$this->assertContains($this->otherFile->getId(), $otherIds);
}

public function testLoadFilesByCategoryMarksCheckedFiles(): void {
[$rules, $wordlists, $other] = FileUtils::loadFilesByCategory($this->user, [$this->file->getId()]);

$checkedIds = [];
foreach (array_merge($rules, $wordlists, $other) as $set) {
$data = $set->getAllValues();
if ($data['checked'] === '1') {
$checkedIds[] = $data['file']->getId();
}
}

$this->assertContains($this->file->getId(), $checkedIds);
}

public function testDeleteThrowsForInvalidId(): void {
$this->expectException(HTException::class);
FileUtils::delete(-1, $this->user);
}

public function testDeleteThrowsWhenFileInUseByTask(): void {
$this->expectException(HTException::class);

$hashType = $this->createHashType();
$hashlist = $this->createHashlist($this->group, $hashType);
$crackerBinaryType = $this->createCrackerBinaryType();
$crackerBinary = $this->createCrackerBinary($crackerBinaryType);
$taskWrapper = $this->createTaskWrapper($this->group, $hashlist);
$task = $this->createTask($taskWrapper, $crackerBinary, $crackerBinaryType);
$this->createFileTask($this->file, $task);

FileUtils::delete($this->file->getId(), $this->user);
}
}
74 changes: 74 additions & 0 deletions ci/phpunit/inc/utils/HashtypeUtilsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace Hashtopolis\inc\utils;

use Hashtopolis\dba\Factory;
use Hashtopolis\dba\models\User;
use Hashtopolis\inc\apiv2\error\HttpError;
use Hashtopolis\inc\HTException;
use Hashtopolis\TestBase;
use Override;

require_once(dirname(__FILE__) . '/../../TestBase.php');
require_once(dirname(__FILE__) . '/../../../../src/inc/startup/include.php');

final class HashtypeUtilsTest extends TestBase {
private User $user;

#[Override]
protected function setUp(): void {
parent::setUp();
$this->user = $this->createUser('ht_user');
}

public function testAddHashtypeCreatesNewHashtype(): void {
$hashtypeId = 999001;
$description = 'test_hashtype_' . uniqid();

$hashtype = HashtypeUtils::addHashtype($hashtypeId, $description, 0, false, $this->user);

$this->assertSame($hashtypeId, $hashtype->getId());
$this->assertStringContainsString($description, $hashtype->getDescription());

Factory::getHashTypeFactory()->delete($hashtype);
}

public function testAddHashtypeThrowsForDuplicateId(): void {
$existing = $this->createHashType();

$this->expectException(HttpError::class);
HashtypeUtils::addHashtype($existing->getId(), 'new_desc', 0, false, $this->user);
}

public function testAddHashtypeThrowsForEmptyDescription(): void {
$this->expectException(HttpError::class);
HashtypeUtils::addHashtype(999003, '', 0, false, $this->user);
}

public function testAddHashtypeThrowsForNegativeId(): void {
$this->expectException(HttpError::class);
HashtypeUtils::addHashtype(-1, 'desc', 0, false, $this->user);
}

public function testDeleteHashtypeRemovesHashtype(): void {
$hashtype = $this->createHashType();

HashtypeUtils::deleteHashtype($hashtype->getId());

$this->assertNull(Factory::getHashTypeFactory()->get($hashtype->getId()));
}

public function testDeleteHashtypeThrowsForInvalidId(): void {
$this->expectException(HTException::class);
HashtypeUtils::deleteHashtype(-1);
}

public function testDeleteHashtypeThrowsWhenHashlistsExist(): void {
$hashtype = $this->createHashType();
$accessGroup = $this->createAccessGroup('ht_del');
$this->createHashlist($accessGroup, $hashtype);

$this->expectException(HTException::class);
HashtypeUtils::deleteHashtype($hashtype->getId());
}
}
Loading
Loading