Compare commits

...

3 Commits

Author SHA1 Message Date
Cameron
06fc8260d5 quick fix to ComicControllerTests 2024-09-08 14:16:11 -05:00
Cameron
a69cd6a33c fixed and reorganized tests 2024-09-08 14:08:48 -05:00
Cameron
fd92d38cdd Added first controller unit test as well as several mock services 2024-09-08 13:17:54 -05:00
12 changed files with 458 additions and 194 deletions

View File

@@ -313,6 +313,8 @@ public class ComicController(ComicsContext context, ILogger<ComicController> log
var comic = await _context.Comics.SingleOrDefaultAsync(c => c.Handle == handle);
if (comic is null)
return NotFound(RequestError.ComicNotFound);
//can't mock this easily, move to a service?
//maybe IComicAnalyzer should be used even just to read a file
var data = await System.IO.File.ReadAllBytesAsync(Path.Join(_config.LibraryRoot, comic.Filepath));
return File(data, "application/octet-stream", new FileInfo(comic.Filepath).Name);
}
@@ -469,8 +471,8 @@ public class ComicController(ComicsContext context, ILogger<ComicController> log
public async Task<IActionResult> GetLibraryStats()
{
return Ok(new LibraryResponse(
await _context.Comics.CountAsync(),
await _context.Comics.Select(c => c.FileXxhash64).Distinct().CountAsync()
ComicCount: await _context.Comics.CountAsync(),
UniqueFiles: await _context.Comics.Select(c => c.FileXxhash64).Distinct().CountAsync()
));
}
}

View File

@@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ComiServ.Entities;
using ComiServ.Services;
namespace ComiServUnitTest;
[TestClass]
public class AuthenticationServiceTests
{
[TestMethod]
public void FailAuth()
{
IAuthenticationService auth = new AuthenticationService();
Assert.IsFalse(auth.Tested);
auth.FailAuth();
Assert.IsTrue(auth.Tested);
Assert.IsNull(auth.User);
}
[TestMethod]
public void AuthenticateUser()
{
IAuthenticationService auth = new AuthenticationService();
User user = new()
{
Username = "NewUser",
UserTypeId = UserTypeEnum.User,
};
Assert.IsFalse(auth.Tested);
auth.Authenticate(user);
Assert.IsTrue(auth.Tested);
Assert.IsNotNull(auth.User);
Assert.AreSame(user, auth.User);
}
}

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ComiServ;
using ComiServ.Entities;
using ComiServ.Services;
using ComiServ.Controllers;
using Microsoft.CodeAnalysis.Elfie.Diagnostics;
using Microsoft.Extensions.Logging;
using ComiServUnitTest.Mocks;
using ComiServ.Background;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
namespace ComiServUnitTest.ContollerTests;
[TestClass]
public class ComicControllerTests
{
private ILoggerFactory? _factory;
public ILoggerFactory factory
{ get
{
_factory = _factory ?? LoggerFactory.Create(builder => builder.AddConsole());
return _factory;
}
}
[TestMethod]
public async Task TestGetPage()
{
const int PAGE_NUMBER = 7;
const string PAGE_FILEPATH = "Page File.jpg";
const string PAGE_MIME = "image/jpeg";
var context = DatabaseHelper.CreateDatabase();
Comic comic = new()
{
Title = "Test Comic",
Filepath = Path.Join("test", "filepath"),
Handle = string.Join("", Enumerable.Repeat("A", ComicsContext.HANDLE_LENGTH)),
Description = ""
};
context.Comics.Add(comic);
User user = new()
{
Username = "user",
UserTypeId = UserTypeEnum.User,
Salt = User.MakeSalt(),
HashedPassword = User.Hash([], []),
};
context.Users.Add(user);
context.SaveChanges();
Configuration config = new()
{
AutoScanPeriodHours = 24,
DatabaseFile = "",
LibraryRoot = "root",
};
ComicPage comicPage = new(PAGE_FILEPATH, PAGE_MIME, [1, 2, 3, 4, 5]);
MockComicAnalyzer analyzer = new();
analyzer.ComicPages.Add((Path.Join(config.LibraryRoot, comic.Filepath), PAGE_NUMBER), comicPage);
//returned from all MockPictureConverter functions
byte[] mockPictureData = [5, 4, 3, 2, 1];
MockPictureConverter converter = new MockPictureConverter(mockPictureData);
AuthenticationService auth = new();
auth.Authenticate(user);
var controller = new ComicController(
context,
factory.CreateLogger<ComicController>(),
new MockConfig(config),
analyzer,
converter,
auth
);
//get actual page
var result = await controller.GetComicPage(comic.Handle, PAGE_NUMBER, null, null, null);
Assert.IsInstanceOfType<FileContentResult>(result);
var contents = ((FileContentResult)result).FileContents;
Assert.IsTrue(comicPage.Data.SequenceEqual(contents));
//invalid handle (too short)
var result2 = await controller.GetComicPage(comic.Handle.Substring(comic.Handle.Length-1),
PAGE_NUMBER, null, null, null);
Assert.IsInstanceOfType<BadRequestObjectResult>(result2);
//valid handle but doesn't exist
var result3 = await controller.GetComicPage(string.Join("", Enumerable.Repeat("B", ComicsContext.HANDLE_LENGTH)),
PAGE_NUMBER, null, null, null);
Assert.IsInstanceOfType<NotFoundObjectResult>(result3);
//valid handle and convert
var result4 = await controller.GetComicPage(comic.Handle, PAGE_NUMBER, 500, 500, PictureFormats.Webp);
Assert.AreEqual(1, converter.ResizeIfBiggerCount);
Assert.IsInstanceOfType<FileContentResult>(result4);
Assert.IsTrue(mockPictureData.SequenceEqual(((FileContentResult)result4).FileContents));
}
}

View File

@@ -0,0 +1,25 @@
using ComiServ;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ComiServUnitTest;
internal static class DatabaseHelper
{
public static ComicsContext CreateDatabase()
{
var connection = new SqliteConnection("Filename=:memory:");
connection.Open();
var contextOptions = new DbContextOptionsBuilder<ComicsContext>()
.UseSqlite(connection)
.Options;
var context = new ComicsContext(contextOptions);
context.Database.EnsureCreated();
return context;
}
}

View File

@@ -0,0 +1,87 @@
using ComiServ.Models;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using System.CodeDom;
using System.Security.Policy;
namespace ComiServUnitTest.EntityTests;
[TestClass]
public class PaginatedTests
{
[TestMethod]
public void PageUnderUnderflow()
{
const int pageNum = 1;
const int pageSize = 20;
const int dataSize = 30;
var data = Enumerable.Range(0, dataSize).ToArray();
var page = new Paginated<int>(pageSize, pageNum, data);
Assert.IsTrue(page.Last);
Assert.AreEqual(pageSize, page.Max);
Assert.AreEqual(pageNum, page.Page);
Assert.AreEqual(10, page.Count);
Assert.AreEqual(page.Items.Count, page.Count);
Assert.AreEqual(20, page.Items.First());
Assert.AreEqual(data.Last(), page.Items.Last());
}
[TestMethod]
public void PageOverflow()
{
const int pageNum = 2;
const int pageSize = 30;
const int dataSize = 150;
var data = Enumerable.Range(0, dataSize).ToArray();
var page = new Paginated<int>(pageSize, pageNum, data);
Assert.IsFalse(page.Last);
Assert.AreEqual(pageSize, page.Max);
Assert.AreEqual(pageNum, page.Page);
Assert.AreEqual(pageSize, page.Count);
Assert.AreEqual(page.Items.Count, page.Count);
Assert.AreEqual(pageSize * pageNum, page.Items.First());
Assert.AreEqual(pageSize * (pageNum + 1) - 1, page.Items.Last());
}
[TestMethod]
public void PageExact()
{
const int pageNum = 1;
const int pageSize = 30;
const int dataSize = 60;
var data = Enumerable.Range(0, dataSize).ToArray();
var page = new Paginated<int>(pageSize, pageNum, data);
Assert.IsTrue(page.Last);
Assert.AreEqual(pageSize, page.Max);
Assert.AreEqual(pageNum, page.Page);
Assert.AreEqual(pageSize, page.Count);
Assert.AreEqual(page.Items.Count, page.Count);
Assert.AreEqual(pageSize * pageNum, page.Items.First());
Assert.AreEqual(data.Last(), page.Items.Last());
}
[TestMethod]
public void PageEmpty()
{
const int pageNum = 0;
const int pageSize = 10;
const int dataSize = 0;
var data = Enumerable.Range(0, dataSize).ToArray();
var page = new Paginated<int>(pageSize, pageNum, data);
Assert.IsTrue(page.Last);
Assert.AreEqual(pageSize, page.Max);
Assert.AreEqual(pageNum, page.Page);
Assert.AreEqual(page.Items.Count, page.Count);
Assert.IsFalse(page.Items.Any());
}
[TestMethod]
public void PageDoesntExist()
{
const int pageNum = 5;
const int pageSize = 20;
const int dataSize = 50;
var data = Enumerable.Range(0, dataSize).ToArray();
var page = new Paginated<int>(pageSize, pageNum, data);
Assert.IsTrue(page.Last);
Assert.AreEqual(pageSize, page.Max);
Assert.AreEqual(pageNum, page.Page);
Assert.AreEqual(page.Items.Count, page.Count);
Assert.IsFalse(page.Items.Any());
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ComiServ.Models;
namespace ComiServUnitTest.EntityTests;
[TestClass]
public class TruncatedTests
{
[TestMethod]
public void TruncationUnderflow()
{
const int truncMax = 20;
const int dataSize = 10;
var data = Enumerable.Range(0, dataSize).ToArray();
var trunc = new Truncated<int>(truncMax, data);
Assert.IsTrue(trunc.Complete);
Assert.AreEqual(truncMax, trunc.Max);
Assert.AreEqual(dataSize, trunc.Count);
Assert.AreEqual(trunc.Items.Count, trunc.Count);
Assert.AreEqual(data.First(), trunc.Items.First());
Assert.AreEqual(data.Last(), trunc.Items.Last());
}
[TestMethod]
public void TruncationOverflow()
{
const int truncMax = 20;
const int dataSize = 30;
var data = Enumerable.Range(0, dataSize).ToArray();
var trunc = new Truncated<int>(truncMax, data);
Assert.IsFalse(trunc.Complete);
Assert.AreEqual(truncMax, trunc.Max);
Assert.AreEqual(truncMax, trunc.Count);
Assert.AreEqual(trunc.Items.Count, trunc.Count);
Assert.AreEqual(data.First(), trunc.Items.First());
Assert.AreEqual(truncMax - 1, trunc.Items.Last());
}
[TestMethod]
public void TruncationExact()
{
const int truncMax = 5;
const int dataSize = 5;
var data = Enumerable.Range(0, dataSize).ToArray();
var trunc = new Truncated<int>(truncMax, data);
Assert.IsTrue(trunc.Complete);
Assert.AreEqual(truncMax, trunc.Max);
Assert.AreEqual(truncMax, trunc.Count);
Assert.AreEqual(trunc.Items.Count, trunc.Count);
Assert.AreEqual(data.First(), trunc.Items.First());
Assert.AreEqual(data.Last(), trunc.Items.Last());
}
[TestMethod]
public void TruncationEmpty()
{
const int truncMax = 5;
const int dataSize = 0;
var data = Enumerable.Range(0, dataSize).ToArray();
var trunc = new Truncated<int>(truncMax, data);
Assert.IsTrue(trunc.Complete);
Assert.AreEqual(truncMax, trunc.Max);
Assert.AreEqual(0, trunc.Count);
Assert.AreEqual(trunc.Items.Count, trunc.Count);
}
}

View File

@@ -0,0 +1,75 @@
using ComiServ.Background;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ComiServUnitTest.Mocks
{
internal class MockComicAnalyzer : IComicAnalyzer
{
//preseed these
public readonly Dictionary<string, ComicAnalysis> AnalysisResults = [];
public readonly HashSet<string> ComicsThatExist = [];
public readonly Dictionary<(string, int), ComicPage> ComicPages = [];
//check these afterwards
public readonly List<string> Analyzed = [];
public readonly List<string> CheckedForExistance = [];
public readonly List<string> Deleted = [];
public readonly List<(string, int)> RetreivedPages = [];
public MockComicAnalyzer()
{
}
public void Clear()
{
Analyzed.Clear();
CheckedForExistance.Clear();
Deleted.Clear();
RetreivedPages.Clear();
}
public ComicAnalysis? AnalyzeComic(string filename)
{
Analyzed.Add(filename);
if (AnalysisResults.TryGetValue(filename, out var analysis))
return analysis;
return null;
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public async Task<ComicAnalysis?> AnalyzeComicAsync(string filename)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
return AnalyzeComic(filename);
}
public bool ComicFileExists(string filename)
{
CheckedForExistance.Add(filename);
return ComicsThatExist.Contains(filename);
}
public void DeleteComicFile(string filename)
{
Deleted.Add(filename);
}
public ComicPage? GetComicPage(string filepath, int page)
{
var key = (filepath, page);
RetreivedPages.Add(key);
if (ComicPages.TryGetValue(key, out var comicPage))
return comicPage;
return null;
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public async Task<ComicPage?> GetComicPageAsync(string filepath, int page)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
return GetComicPage(filepath, page);
}
}
}

View File

@@ -0,0 +1,20 @@
using ComiServ.Services;
using Microsoft.Build.Framework;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ComiServUnitTest.Mocks;
internal class MockConfig : IConfigService
{
private readonly Configuration _Config;
public Configuration Config => _Config.Copy();
public MockConfig(Configuration config)
{
_Config = config;
}
}

View File

@@ -0,0 +1,46 @@
using ComiServ.Background;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ComiServUnitTest.Mocks
{
internal class MockPictureConverter : IPictureConverter
{
public int MakeThumbnailCount = 0;
public int ResizeCount = 0;
public int ResizeIfBiggerCount = 0;
private byte[] StreamObject { get; }
public MockPictureConverter(byte[] streamObject)
{
StreamObject = streamObject;
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public async Task<Stream> MakeThumbnail(Stream image)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
MakeThumbnailCount++;
return new MemoryStream(StreamObject);
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public async Task<Stream> Resize(Stream image, Size newSize, PictureFormats? newFormat = null)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
ResizeCount++;
return new MemoryStream(StreamObject);
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public async Task<Stream> ResizeIfBigger(Stream image, Size maxSize, PictureFormats? newFormat = null)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
ResizeIfBiggerCount++;
return new MemoryStream(StreamObject);
}
}
}

View File

@@ -1,87 +0,0 @@
using ComiServ.Models;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using System.CodeDom;
using System.Security.Policy;
namespace ComiServUnitTest;
[TestClass]
public class PaginatedTests
{
[TestMethod]
public void PageUnderUnderflow()
{
const int pageNum = 1;
const int pageSize = 20;
const int dataSize = 30;
var data = Enumerable.Range(0, dataSize).ToArray();
var page = new Paginated<int>(pageSize, pageNum, data);
Assert.IsTrue(page.Last);
Assert.AreEqual(pageSize, page.Max);
Assert.AreEqual(pageNum, page.Page);
Assert.AreEqual(10, page.Count);
Assert.AreEqual(page.Items.Count, page.Count);
Assert.AreEqual(20, page.Items.First());
Assert.AreEqual(data.Last(), page.Items.Last());
}
[TestMethod]
public void PageOverflow()
{
const int pageNum = 2;
const int pageSize = 30;
const int dataSize = 150;
var data = Enumerable.Range(0, dataSize).ToArray();
var page = new Paginated<int>(pageSize, pageNum, data);
Assert.IsFalse(page.Last);
Assert.AreEqual(pageSize, page.Max);
Assert.AreEqual(pageNum, page.Page);
Assert.AreEqual(pageSize, page.Count);
Assert.AreEqual(page.Items.Count, page.Count);
Assert.AreEqual(pageSize*pageNum, page.Items.First());
Assert.AreEqual(pageSize*(pageNum+1)-1, page.Items.Last());
}
[TestMethod]
public void PageExact()
{
const int pageNum = 1;
const int pageSize = 30;
const int dataSize = 60;
var data = Enumerable.Range(0, dataSize).ToArray();
var page = new Paginated<int>(pageSize, pageNum, data);
Assert.IsTrue(page.Last);
Assert.AreEqual(pageSize, page.Max);
Assert.AreEqual(pageNum, page.Page);
Assert.AreEqual(pageSize, page.Count);
Assert.AreEqual(page.Items.Count, page.Count);
Assert.AreEqual(pageSize * pageNum, page.Items.First());
Assert.AreEqual(data.Last(), page.Items.Last());
}
[TestMethod]
public void PageEmpty()
{
const int pageNum = 0;
const int pageSize = 10;
const int dataSize = 0;
var data = Enumerable.Range(0, dataSize).ToArray();
var page = new Paginated<int>(pageSize, pageNum, data);
Assert.IsTrue(page.Last);
Assert.AreEqual(pageSize, page.Max);
Assert.AreEqual(pageNum, page.Page);
Assert.AreEqual(page.Items.Count, page.Count);
Assert.IsFalse(page.Items.Any());
}
[TestMethod]
public void PageDoesntExist()
{
const int pageNum = 5;
const int pageSize = 20;
const int dataSize = 50;
var data = Enumerable.Range(0, dataSize).ToArray();
var page = new Paginated<int>(pageSize, pageNum, data);
Assert.IsTrue(page.Last);
Assert.AreEqual(pageSize, page.Max);
Assert.AreEqual(pageNum, page.Page);
Assert.AreEqual(page.Items.Count, page.Count);
Assert.IsFalse(page.Items.Any());
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ComiServ.Entities;
using ComiServ.Services;
namespace ComiServUnitTest.ServiceTests;
[TestClass]
public class AuthenticationServiceTests
{
[TestMethod]
public void FailAuth()
{
IAuthenticationService auth = new AuthenticationService();
Assert.IsFalse(auth.Tested);
auth.FailAuth();
Assert.IsTrue(auth.Tested);
Assert.IsNull(auth.User);
}
[TestMethod]
public void AuthenticateUser()
{
IAuthenticationService auth = new AuthenticationService();
User user = new()
{
Username = "NewUser",
UserTypeId = UserTypeEnum.User,
};
Assert.IsFalse(auth.Tested);
auth.Authenticate(user);
Assert.IsTrue(auth.Tested);
Assert.IsNotNull(auth.User);
Assert.AreSame(user, auth.User);
}
}

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ComiServ.Models;
namespace ComiServUnitTest;
[TestClass]
public class TruncatedTests
{
[TestMethod]
public void TruncationUnderflow()
{
const int truncMax = 20;
const int dataSize = 10;
var data = Enumerable.Range(0, dataSize).ToArray();
var trunc = new Truncated<int>(truncMax, data);
Assert.IsTrue(trunc.Complete);
Assert.AreEqual(truncMax, trunc.Max);
Assert.AreEqual(dataSize, trunc.Count);
Assert.AreEqual(trunc.Items.Count, trunc.Count);
Assert.AreEqual(data.First(), trunc.Items.First());
Assert.AreEqual(data.Last(), trunc.Items.Last());
}
[TestMethod]
public void TruncationOverflow()
{
const int truncMax = 20;
const int dataSize = 30;
var data = Enumerable.Range(0, dataSize).ToArray();
var trunc = new Truncated<int>(truncMax, data);
Assert.IsFalse(trunc.Complete);
Assert.AreEqual(truncMax, trunc.Max);
Assert.AreEqual(truncMax, trunc.Count);
Assert.AreEqual(trunc.Items.Count, trunc.Count);
Assert.AreEqual(data.First(), trunc.Items.First());
Assert.AreEqual(truncMax - 1, trunc.Items.Last());
}
[TestMethod]
public void TruncationExact()
{
const int truncMax = 5;
const int dataSize = 5;
var data = Enumerable.Range(0, dataSize).ToArray();
var trunc = new Truncated<int>(truncMax, data);
Assert.IsTrue(trunc.Complete);
Assert.AreEqual(truncMax, trunc.Max);
Assert.AreEqual(truncMax, trunc.Count);
Assert.AreEqual(trunc.Items.Count, trunc.Count);
Assert.AreEqual(data.First(), trunc.Items.First());
Assert.AreEqual(data.Last(), trunc.Items.Last());
}
[TestMethod]
public void TruncationEmpty()
{
const int truncMax = 5;
const int dataSize = 0;
var data = Enumerable.Range(0, dataSize).ToArray();
var trunc = new Truncated<int>(truncMax, data);
Assert.IsTrue(trunc.Complete);
Assert.AreEqual(truncMax, trunc.Max);
Assert.AreEqual(0, trunc.Count);
Assert.AreEqual(trunc.Items.Count, trunc.Count);
}
}