Skip to content
Merged
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
126 changes: 126 additions & 0 deletions src/BitCheck.Tests/ApplicationTests/AddOperationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using BitCheck.Application;
using BitCheck.Database;

namespace BitCheck.Tests.ApplicationTests
{
[TestClass]
public class AddOperationTests : ApplicationTestBase
{
[TestMethod]
public void AddDisabled_DoesNotInsertNewEntries()
{
var filePath = Path.Combine(_testDir, "untracked.txt");
File.WriteAllText(filePath, "data");

var options = new AppOptions(
Recursive: false,
Add: false,
Update: true,
Check: false,
Verbose: false,
Strict: false,
Timestamps: false,
SingleDatabase: true,
File: null,
Delete: false,
Info: false,
List: false);

RunApp(options, _testDir);

var dbPath = Path.Combine(_testDir, BitCheckConstants.DatabaseFileName);
using var db = new DatabaseService(dbPath);
Assert.IsNull(db.GetFileEntry(Path.GetFileName(filePath)), "File should not be added when --add is false");
Assert.AreEqual(0, db.GetAllEntries().Count(), "Database should remain empty without add option");
}

[TestMethod]
public void AddOnly_SkipsExistingFilesWithoutHashing()
{
var filePath = Path.Combine(_testDir, "existing.txt");
File.WriteAllText(filePath, "original content");

var addOptions = new AppOptions(
Recursive: false,
Add: true,
Update: false,
Check: false,
Verbose: true,
Strict: false,
Timestamps: false,
SingleDatabase: true,
File: null,
Delete: false,
Info: false,
List: false);

RunApp(addOptions, _testDir);

var dbPath = Path.Combine(_testDir, BitCheckConstants.DatabaseFileName);
string originalHash;
using (var db = new DatabaseService(dbPath))
{
var entry = db.GetFileEntry(Path.GetFileName(filePath));
Assert.IsNotNull(entry, "File should be added initially");
originalHash = entry.Hash;
}

using var capture = new StringWriter();
RunApp(addOptions, _testDir, capture);
var output = capture.ToString();

StringAssert.Contains(output, "[SKIP]", "Existing file should be skipped on second --add run");
StringAssert.Contains(output, "Already in database", "Skip message should indicate file is already tracked");

using (var db = new DatabaseService(dbPath))
{
var entry = db.GetFileEntry(Path.GetFileName(filePath));
Assert.AreEqual(originalHash, entry!.Hash, "Hash should remain unchanged");
}
}

[TestMethod]
public void AddWithCheck_DoesNotSkipExistingFiles()
{
var filePath = Path.Combine(_testDir, "checked.txt");
File.WriteAllText(filePath, "content");

var addOptions = new AppOptions(
Recursive: false,
Add: true,
Update: false,
Check: false,
Verbose: true,
Strict: false,
Timestamps: false,
SingleDatabase: true,
File: null,
Delete: false,
Info: false,
List: false);

RunApp(addOptions, _testDir);

var addCheckOptions = new AppOptions(
Recursive: false,
Add: true,
Update: false,
Check: true,
Verbose: true,
Strict: false,
Timestamps: false,
SingleDatabase: true,
File: null,
Delete: false,
Info: false,
List: false);

using var capture = new StringWriter();
RunApp(addCheckOptions, _testDir, capture);
var output = capture.ToString();

StringAssert.Contains(output, "[OK]", "Existing file should be checked when --check is specified");
Assert.DoesNotContain("Already in database", output, "File should not be skipped when --check is active");
}
}
}
61 changes: 61 additions & 0 deletions src/BitCheck.Tests/ApplicationTests/ApplicationTestBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using BitCheck.Application;
using BitCheck.Database;

namespace BitCheck.Tests.ApplicationTests
{
/// <summary>
/// Base class for BitCheckApplication tests providing common setup, teardown, and helper methods.
/// </summary>
public abstract class ApplicationTestBase
{
protected string _testDir = null!;
protected string _originalWorkingDirectory = null!;

[TestInitialize]
public void Setup()
{
_testDir = Path.Combine(Path.GetTempPath(), $"bitcheck_app_test_{Guid.NewGuid()}");
Directory.CreateDirectory(_testDir);
_originalWorkingDirectory = Directory.GetCurrentDirectory();
}

[TestCleanup]
public void Cleanup()
{
Directory.SetCurrentDirectory(_originalWorkingDirectory);

if (Directory.Exists(_testDir))
{
Directory.Delete(_testDir, true);
}
}

protected static void RunApp(AppOptions options, string workingDirectory, StringWriter? consoleCapture = null)
{
var previous = Directory.GetCurrentDirectory();
var previousOut = Console.Out;
var previousErr = Console.Error;
Directory.SetCurrentDirectory(workingDirectory);
if (consoleCapture != null)
{
Console.SetOut(consoleCapture);
Console.SetError(consoleCapture);
}
try
{
var app = new BitCheckApplication(options);
app.Run();
}
finally
{
if (consoleCapture != null)
{
consoleCapture.Flush();
Console.SetOut(previousOut);
Console.SetError(previousErr);
}
Directory.SetCurrentDirectory(previous);
}
}
}
}
55 changes: 55 additions & 0 deletions src/BitCheck.Tests/ApplicationTests/CheckOperationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using BitCheck.Application;
using BitCheck.Database;

namespace BitCheck.Tests.ApplicationTests
{
[TestClass]
public class CheckOperationTests : ApplicationTestBase
{
[TestMethod]
public void MissingFiles_WithCheckOnly_RetainEntries()
{
var filePath = Path.Combine(_testDir, "orphan.txt");
File.WriteAllText(filePath, "data");

var addOptions = new AppOptions(
Recursive: false,
Add: true,
Update: false,
Check: false,
Verbose: false,
Strict: false,
Timestamps: false,
SingleDatabase: true,
File: null,
Delete: false,
Info: false,
List: false);

RunApp(addOptions, _testDir);

File.Delete(filePath);

var checkOptions = new AppOptions(
Recursive: false,
Add: false,
Update: false,
Check: true,
Verbose: false,
Strict: false,
Timestamps: false,
SingleDatabase: true,
File: null,
Delete: false,
Info: false,
List: false);

RunApp(checkOptions, _testDir);

var dbPath = Path.Combine(_testDir, BitCheckConstants.DatabaseFileName);
using var db = new DatabaseService(dbPath);
var entry = db.GetFileEntry(Path.GetFileName(filePath));
Assert.IsNotNull(entry, "Entry should remain when update is false");
}
}
}
128 changes: 128 additions & 0 deletions src/BitCheck.Tests/ApplicationTests/DeleteOperationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using BitCheck.Application;

namespace BitCheck.Tests.ApplicationTests
{
[TestClass]
public class DeleteOperationTests : ApplicationTestBase
{
[TestMethod]
public void DeleteWithoutFile_ShowsError()
{
var options = new AppOptions(
Recursive: false,
Add: false,
Update: false,
Check: false,
Verbose: false,
Strict: false,
Timestamps: false,
SingleDatabase: false,
File: null,
Delete: true,
Info: false,
List: false);

using var capture = new StringWriter();
RunApp(options, _testDir, capture);
var output = capture.ToString();

StringAssert.Contains(output, "Error:", "Should show error");
StringAssert.Contains(output, "--delete can only be used with --file", "Error should explain delete requires file");
}

[TestMethod]
public void RecursiveWithFile_ShowsError()
{
var filePath = Path.Combine(_testDir, "test.txt");
File.WriteAllText(filePath, "content");

var options = new AppOptions(
Recursive: true,
Add: true,
Update: false,
Check: false,
Verbose: false,
Strict: false,
Timestamps: false,
SingleDatabase: false,
File: filePath,
Delete: false,
Info: false,
List: false);

using var capture = new StringWriter();
RunApp(options, _testDir, capture);
var output = capture.ToString();

StringAssert.Contains(output, "Error:", "Should show error");
StringAssert.Contains(output, "--recursive cannot be used with --file", "Error should explain recursive is invalid with file");
}

[TestMethod]
public void DeleteWithOtherOperations_ShowsError()
{
var filePath = Path.Combine(_testDir, "test.txt");
File.WriteAllText(filePath, "content");

// Test --delete with --add
var deleteWithAdd = new AppOptions(
Recursive: false,
Add: true,
Update: false,
Check: false,
Verbose: false,
Strict: false,
Timestamps: false,
SingleDatabase: false,
File: filePath,
Delete: true,
Info: false,
List: false);

using var capture1 = new StringWriter();
RunApp(deleteWithAdd, _testDir, capture1);
StringAssert.Contains(capture1.ToString(), "--delete cannot be combined with other operations",
"Should reject --delete with --add");

// Test --delete with --update
var deleteWithUpdate = new AppOptions(
Recursive: false,
Add: false,
Update: true,
Check: false,
Verbose: false,
Strict: false,
Timestamps: false,
SingleDatabase: false,
File: filePath,
Delete: true,
Info: false,
List: false);

using var capture2 = new StringWriter();
RunApp(deleteWithUpdate, _testDir, capture2);
StringAssert.Contains(capture2.ToString(), "--delete cannot be combined with other operations",
"Should reject --delete with --update");

// Test --delete with --check
var deleteWithCheck = new AppOptions(
Recursive: false,
Add: false,
Update: false,
Check: true,
Verbose: false,
Strict: false,
Timestamps: false,
SingleDatabase: false,
File: filePath,
Delete: true,
Info: false,
List: false);

using var capture3 = new StringWriter();
RunApp(deleteWithCheck, _testDir, capture3);
StringAssert.Contains(capture3.ToString(), "--delete cannot be combined with other operations",
"Should reject --delete with --check");
}
}
}
Loading