diff --git a/Source Code/.vscode/launch.json b/Source Code/.vscode/launch.json deleted file mode 100644 index c4a836b..0000000 --- a/Source Code/.vscode/launch.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - // Use IntelliSense to find out which attributes exist for C# debugging - // Use hover for the description of the existing attributes - // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md - "name": ".NET Core Launch (console)", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/main/bin/Debug/net6.0/main.dll", - "args": [], - "cwd": "${workspaceFolder}/main", - // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console - "console": "internalConsole", - "stopAtEntry": false - }, - { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach" - } - ] -} \ No newline at end of file diff --git a/Source Code/.vscode/tasks.json b/Source Code/.vscode/tasks.json deleted file mode 100644 index 2754b32..0000000 --- a/Source Code/.vscode/tasks.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "${workspaceFolder}/main/main.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "publish", - "command": "dotnet", - "type": "process", - "args": [ - "publish", - "${workspaceFolder}/main/main.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "watch", - "command": "dotnet", - "type": "process", - "args": [ - "watch", - "run", - "${workspaceFolder}/main/main.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.ArchiveTests/ArchivingTests.cs b/Source Code/TeamHobby.HobbyProjectGenerator.ArchiveTests/ArchivingTests.cs deleted file mode 100644 index 3d80763..0000000 --- a/Source Code/TeamHobby.HobbyProjectGenerator.ArchiveTests/ArchivingTests.cs +++ /dev/null @@ -1,344 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Odbc; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using TeamHobby.HobbyProjectGenerator.Archive; -using TeamHobby.HobbyProjectGenerator.DataAccess; - -namespace TeamHobby.HobbyProjectGenerator.ArchiveTests -{ - public class ArchivingTests - { - //private string dbInfo = "DRIVER={MariaDB ODBC 3.1 Driver};" + - // "SERVER=localhost;" + - // "DATABASE=hobby;" + - // "UID=root;" + - // "PASSWORD=Teamhobby;" + - // "OPTION=3"; - - - // [TestMethod] - public void IsArchiveFolderCreated(IDataSource sqlDAO) - { - // Arrange - //SqlDAO sqlDAO = new SqlDAO(dbInfo); - ArchiveManager archiveManager = new ArchiveManager(sqlDAO); - string folderPath = @"C:/HobbyArchive"; - bool expectedVal = true; - - // Act - archiveManager.CreateArchiveFolder(); - - - // Assert - bool actualVal = Directory.Exists(folderPath); - if (expectedVal == actualVal) - { - Console.WriteLine("Expected: {0}, Actual: {1}. Folder created correctly", expectedVal, actualVal); - try - { - Directory.Delete(folderPath, true); - } - catch { - Console.WriteLine("Folder failed to be deleted"); - } - } - else - { - Console.WriteLine("Expected: {0}, Actual: {1}. Folder created incorrectly", expectedVal, actualVal); - } - } - - // [TestMethod] - public void IsFileNameCreated(IDataSource sqlDAO) - { - // Arrange - string foldPath = @"C:\HobbyArchive"; - string fileName = DateTime.Now.ToString("M_d_yyyy") + "_archive.csv"; - string expectedFilePath = System.IO.Path.Combine(foldPath, fileName); - ArchiveManager archiveManager = new ArchiveManager(sqlDAO); - - // Act - string actualFilePath = archiveManager.CreateOutFileName(); - - // Assert - bool result = String.Equals(actualFilePath, expectedFilePath); - if (result) - { - Console.WriteLine("Expected: {0}, Actual: {1}. File name created correctly", expectedFilePath, actualFilePath); - } - else - { - Console.WriteLine("Expected: {0}, Actual: {1}. File name created incorrectly", expectedFilePath, actualFilePath); - } - - } - - // [Test Method] - public void IsCSVFileExist(IDataSource sqlDAO) - { - // Arrange - ArchiveManager archiveManager = new ArchiveManager(sqlDAO); - SqlDAO sqlDS = null; - string folderPath = @"C:/HobbyArchive"; - - try - { - bool folderRes = archiveManager.CreateArchiveFolder(); - } - catch (Exception ex) - { - Console.WriteLine("Folder creation for copying failed"); - } - string filePath = archiveManager.CreateOutFileName(); - bool expectedVal = true; - - if (sqlDAO.GetType() == typeof(SqlDAO)) - { - sqlDS = (SqlDAO)sqlDAO; - } - - // Act - bool actualVal = false; - if (sqlDS != null) - { - try - { - actualVal = sqlDS.CopyToFile(filePath); - } - catch (Exception ex) - { - Console.WriteLine("Copying to a file failed !!"); - Console.WriteLine(ex.Message); - } - } - // Assert - bool result = File.Exists(filePath); - if (result) - { - Console.WriteLine("Expected: {0}, Actual: {1}. CSV file created correctly", expectedVal, actualVal); - // Clean up the folder use to test. - try - { - Directory.Delete(folderPath, true); - } - catch - { - Console.WriteLine("Folder failed to be deleted"); - } - } - else - { - Console.WriteLine("Expected: {0}, Actual: {1}. CSV file created incorrectly", expectedVal, actualVal); - } - - } - - // [Test Method] - public void IsProcessFlowCompleted(IDataSource sqlDAO) - { - // Arrange - ArchiveManager archive = new ArchiveManager(sqlDAO); - - // Act - // Testing archive Manager - int i = -10; - while (i < 20) - { - DateTime date1 = new DateTime(2021, 12, 1, 0, 0, 0); - //Console.WriteLine("Testing first of month: {0}", date1.ToString("dd")); - //string currentDate = DateTime.Now.ToString("dd"); - //string currentTime = DateTime.Now.ToString("T"); - string currentTime = "00:00:00 AM"; - string currentDate = i.ToString(); - - if (i == 1) - { - currentDate = date1.ToString("dd"); - //ArchiveManager archive = new ArchiveManager(sqlDAO); - //archive.Controller(); - } - - //Console.WriteLine("Current date: {0}, Current Time: {1}", currentDate, currentTime); - Console.WriteLine("Current date: {0}, Current Time: {1}", currentDate, currentTime); - if (String.Equals(currentDate, "01") && String.Equals(currentTime, "00:00:00 AM")) - { - Console.WriteLine("Archiving process Start"); - //ArchiveManager archive = new ArchiveManager(sqlDAO); - archive.Controller(); - } - - i++; - } - - } - - public void IsCleaningUpCompleted(IDataSource sqlDAO) - { - //Arrange - ArchiveManager archive = new ArchiveManager(sqlDAO); - bool actualVal = false; - bool expectedVal = false; - - // Act - actualVal = archive.Controller(); - - // Assert - //bool result = !(actualVal && expectedVal); - if (expectedVal == actualVal) - { - Console.WriteLine("Expected: {0}, Actual: {1}. Error handle correctly", expectedVal, actualVal); - } - else - { - Console.WriteLine("Expected: {0}, Actual: {1}. Error handle incorrectly", expectedVal, actualVal); - } - - } - - public void RemoveEntriesTest(IDataSource sqlDAO) - { - Console.WriteLine("TESTING - REMOVE ENTRIES"); - - // Arrange - ArchiveManager archiveManager = new ArchiveManager(sqlDAO); - SqlDAO sqlDS = null; - - if (sqlDAO.GetType() == typeof(SqlDAO)) - { - sqlDS = (SqlDAO)sqlDAO; - //sqlDS.GetConnection().Open(); - - // Act - Console.WriteLine("\nInserting into archive..."); - sqlDS.WriteData("INSERT into log(LtimeStamp, LvName, catname, userop, logmessage) values " + - "('2021-08-07 23:00:00', 'Info', 'View', 'create some projects', 'new account created')," + - "('2021-06-04 23:00:00', 'Info', 'Business', 'create some projects', 'new projects made')," + - "('2021-07-02 23:00:00', 'Info', 'View', 'log out', 'log out successful')," + - "('2021-09-03 23:00:00', 'Info', 'Business', 'log in', 'log in successfully')," + - "('2021-10-20 23:00:00', 'Info', 'View', 'search for projects', 'result return')," + - "('2021-09-03 23:00:00', 'Info', 'Business', 'log in', 'log in successfully');"); - - OdbcDataReader odbcObj = (OdbcDataReader)sqlDS.ReadData("SELECT * FROM log WHERE DATEDIFF(CURRENT_TIMESTAMP, log.LtimeStamp) > 30;"); - - int count = 0; - while (odbcObj.Read()) { ++count; } - Console.WriteLine("Number of entries > 30 days old: " + count); - sqlDS.GetConnection().Close(); - - Console.WriteLine("\nRemoving entries from archive..."); - sqlDS.RemoveEntries(); - - odbcObj = (OdbcDataReader)sqlDS.ReadData("SELECT * FROM log WHERE DATEDIFF(CURRENT_TIMESTAMP, log.LtimeStamp) > 30;"); - - count = 0; - while (odbcObj.Read()) { ++count; } - Console.WriteLine("Number of entries > 30 days old: " + count); - sqlDS.GetConnection().Close(); - - // Assert - bool expectedVal = true; - bool actualVal = !Convert.ToBoolean(count); - - if (expectedVal == actualVal) - { - Console.WriteLine("\nExpected: {0}, Actual: {0}. Entries removed correctly.", expectedVal, actualVal); - } - else - { - Console.WriteLine("\nExpected: {0}, Actual: {1}. Entries not removed.", expectedVal, actualVal); - } - - //sqlDS.GetConnection().Close(); - } - - return; - } - - public void RemoveOutputFileTest(IDataSource sqlDAO) - { - Console.WriteLine("TESTING - REMOVE OUTPUT FILE\n"); - - // Arrange - ArchiveManager archiveManager = new ArchiveManager(sqlDAO); - SqlDAO sqlDS = null; - - if (sqlDAO.GetType() == typeof(SqlDAO)) - { - sqlDS = (SqlDAO)sqlDAO; - } - - // Act - string filepath = archiveManager.CreateOutFileName(); - Console.WriteLine("\nRemoving file..."); - sqlDS.RemoveOutputFile(filepath); - - // Assert - bool expectedVal = false; - bool actualVal = File.Exists(filepath); - Console.WriteLine("Checking if file exists: " + actualVal); - - if (expectedVal == actualVal) - { - Console.WriteLine("\nExpected: {0}, Actual: {0}. Output file removed correctly.", expectedVal, actualVal); - } - else - { - Console.WriteLine("\nExpected: {0}, Actual: {1}. Output file NOT removed.", expectedVal, actualVal); - } - - return; - } - - public static void Main(string[] args) - { - string dbInfo = "DRIVER={MariaDB ODBC 3.1 Driver};" + - "SERVER=localhost;" + - "DATABASE=hobby;" + - "UID=root;" + - "PASSWORD=Teamhobby;" + - "OPTION=3"; - SqlDAO sqlDAO = new SqlDAO(dbInfo); - sqlDAO.GetConnection().Open(); - //ArchiveManager archiveManager = new ArchiveManager(sqlDAO); - - // Testing folder creation - ArchivingTests test = new ArchivingTests(); - test.IsArchiveFolderCreated(sqlDAO); - Console.WriteLine("-----------------"); - Console.WriteLine(""); - - // Testing file creation - test.IsFileNameCreated(sqlDAO); - Console.WriteLine("-----------------"); - Console.WriteLine(""); - - - // Testing file creation - test.IsCSVFileExist(sqlDAO); - Console.WriteLine("-----------------"); - Console.WriteLine(""); - - - // Testing clean up sequence - //test.IsCleaningUpCompleted(sqlDAO); - //Console.WriteLine("-----------------"); - //Console.WriteLine(""); - - // Testing remove entries - test.RemoveEntriesTest(sqlDAO); - Console.WriteLine("-----------------"); - Console.WriteLine(""); - - // Testing remove output file - test.RemoveOutputFileTest(sqlDAO); - Console.WriteLine("-----------------"); - Console.WriteLine(""); - sqlDAO.GetConnection().Close(); - } - - } - -} diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.ArchiveTests/TeamHobby.HobbyProjectGenerator.ArchiveTests.csproj b/Source Code/TeamHobby.HobbyProjectGenerator.ArchiveTests/TeamHobby.HobbyProjectGenerator.ArchiveTests.csproj deleted file mode 100644 index 560115c..0000000 --- a/Source Code/TeamHobby.HobbyProjectGenerator.ArchiveTests/TeamHobby.HobbyProjectGenerator.ArchiveTests.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - - - - - diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.DAL/IRepository.cs b/Source Code/TeamHobby.HobbyProjectGenerator.DAL/IRepository.cs deleted file mode 100644 index 35d317d..0000000 --- a/Source Code/TeamHobby.HobbyProjectGenerator.DAL/IRepository.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace TeamHobby.HobbyProjectGenerator.DAL -{ - public interface IRepository - { - bool Create(T model); - - T Read(); - - bool Update(T model); - - bool Delete(T model); - } -} diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.DAL/SqlDAO.cs b/Source Code/TeamHobby.HobbyProjectGenerator.DAL/SqlDAO.cs deleted file mode 100644 index 8e331a7..0000000 --- a/Source Code/TeamHobby.HobbyProjectGenerator.DAL/SqlDAO.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.Data.SqlClient; -using TeamHobby.HobbyProjectGenerator.Models; - -namespace TeamHobby.HobbyProjectGenerator.DAL -{ - public class SqlDAO - { - public IList GetUserData(string username) - { - // Sql server connection string, needs to be changed accordingly to connect - var connString = "server=localhost;userid=root;password=Plop20;database=users"; // Using @" " makes the string literal - - // ADO.NET - ODBC - using (var conn = new SqlConnection(connString)) - { - // More complex sql commands are done in this method instead - var sql = "Select * from roles"; - using (var command = new SqlCommand(sql, conn)) - { - // Get the results from the query - SqlDataReader r = command.ExecuteReader(); - - // If you wanted to get a singular value back such as a count of a certain item - //command.ExecuteScalar(); - - // To execute something that doesn't expect results to come back - // Use this method instead, IE. Update command - //command.ExecuteNonQuery(); - - // Read data from query - while (r.Read()) - { - Console.WriteLine(r.ToString()); - } - return null; - } - - /*// this is meant for specific basic sql commands - using (var adapter = new SqlDataAdapter()) - { - adapter.UpdateCommand - adapter.DeleteCommand - adapter.InsertCommand - adapter.SelectCommand - }*/ - - } - } - } -} diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.DAL/TeamHobby.HobbyProjectGenerator.DAL.csproj b/Source Code/TeamHobby.HobbyProjectGenerator.DAL/TeamHobby.HobbyProjectGenerator.DAL.csproj deleted file mode 100644 index 26710ee..0000000 --- a/Source Code/TeamHobby.HobbyProjectGenerator.DAL/TeamHobby.HobbyProjectGenerator.DAL.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - net6.0 - enable - enable - - - - - - - - - - - diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Contracts/ILogger.cs b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Contracts/ILogger.cs index bf17d92..9aee5ba 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Contracts/ILogger.cs +++ b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Contracts/ILogger.cs @@ -1,4 +1,4 @@ -/*using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -14,4 +14,3 @@ namespace TeamHobby.HobbyProjectGenerator.Logging } } -*/ \ No newline at end of file diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Contracts/ILoggerFactory.cs b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Contracts/ILoggerFactory.cs index 28ab00f..cf0d199 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Contracts/ILoggerFactory.cs +++ b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Contracts/ILoggerFactory.cs @@ -1,4 +1,4 @@ -/*using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -14,4 +14,3 @@ namespace TeamHobby.HobbyProjectGenerator.Logging.Contracts } } } -*/ \ No newline at end of file diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/ConsoleLogger.cs b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/ConsoleLogger.cs index ba0c24d..19d2377 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/ConsoleLogger.cs +++ b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/ConsoleLogger.cs @@ -1,16 +1,16 @@ -// using System; -// using System.Collections.Generic; -// using System.Linq; -// using System.Text; -// using System.Threading.Tasks; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; -// namespace TeamHobby.HobbyProjectGenerator.Logging -// { -// public class ConsoleLogger : ILogger -// { -// public bool Log(LogEntry log) -// { -// return true; -// } -// } -// } +namespace TeamHobby.HobbyProjectGenerator.Logging +{ + public class ConsoleLogger : ILogger + { + public bool Log(LogEntry log) + { + return true; + } + } +} diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/DBLogger.cs b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/DBLogger.cs index 59e7d2a..132ee82 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/DBLogger.cs +++ b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/DBLogger.cs @@ -1,4 +1,4 @@ -/*using System; +using System; namespace TeamHobby.HobbyProjectGenerator.Logging { @@ -21,4 +21,4 @@ namespace TeamHobby.HobbyProjectGenerator.Logging } -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/DBLoggerFactory.cs b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/DBLoggerFactory.cs index c996ba2..f790cbb 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/DBLoggerFactory.cs +++ b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/DBLoggerFactory.cs @@ -1,4 +1,4 @@ -/*using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -18,4 +18,3 @@ namespace TeamHobby.HobbyProjectGenerator.Logging.Implementations } } } -*/ \ No newline at end of file diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/FileLogger.cs b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/FileLogger.cs index b4e4a75..6c561b1 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/FileLogger.cs +++ b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/FileLogger.cs @@ -1,23 +1,24 @@ -// using System; +using System; +using TeamHobby.HobbyProjectGenerator.Logging.Contracts; -// namespace TeamHobby.HobbyProjectGenerator.Logging -// { -// public class FileLogger : Ilogger -// { -// public FileLogger() -// { -// } +namespace TeamHobby.HobbyProjectGenerator.Logging +{ + public class FileLogger : ILogger + { + public FileLogger() + { + } -// public IList GetAllLogs() -// { -// throw new NotImplementedException(); -// } + public IList GetAllLogs() + { + throw new NotImplementedException(); + } -// public bool Log(LogEntry log) -// { -// throw new NotImplementedException(); -// } + public bool Log(LogEntry log) + { + throw new NotImplementedException(); + } -// } + } -// } +} diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/InMemoryLogger.cs b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/InMemoryLogger.cs index eea5e87..10cbf74 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/InMemoryLogger.cs +++ b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/Implementations/InMemoryLogger.cs @@ -1,58 +1,58 @@ -// using System; -// using System.Collections.Generic; -// namespace TeamHobby.HobbyProjectGenerator.Logging -// { -// // implementation of ILogger that stores logs as they initialize -// public class InMemoryLogger : ILogger -// { -// // make readonly to prevent it from being changed somewhere other than in the constructor -// private readonly IList _logStore; +using System; +using System.Collections.Generic; +namespace TeamHobby.HobbyProjectGenerator.Logging +{ + // implementation of ILogger that stores logs as they initialize + public class InMemoryLogger : ILogger + { + // make readonly to prevent it from being changed somewhere other than in the constructor + private readonly IList _logStore; -// public InMemoryLogger() -// { -// // list of current logs -// _logStore = new List(); -// } + public InMemoryLogger() + { + // list of current logs + _logStore = new List(); + } -// public InMemoryLogger(IList logStore) -// { -// _logStore = logStore; -// } + public InMemoryLogger(IList logStore) + { + _logStore = logStore; + } -// public bool Log(LogEntry entry) -// { -// if (entry is null) -// { -// throw new ArgumentNullException(nameof(entry)); -// } + public bool Log(LogEntry entry) + { + if (entry is null) + { + throw new ArgumentNullException(nameof(entry)); + } -// try -// { -// // string interpolation to add UTC timestamp to log description -// //_logStore.Add($"{DateTime.UtcNow}-> {description}"); -// return true; -// } -// catch -// { -// return false; -// } -// } -// public IList GetAllLogs() -// { -// return _logStore; -// } + try + { + // string interpolation to add UTC timestamp to log description + //_logStore.Add($"{DateTime.UtcNow}-> {description}"); + return true; + } + catch + { + return false; + } + } + public IList GetAllLogs() + { + return _logStore; + } -// public override bool Equals(object? obj) -// { -// return obj is InMemoryLogger logger && -// EqualityComparer>.Default.Equals(_logStore, logger._logStore); -// } + public override bool Equals(object? obj) + { + return obj is InMemoryLogger logger && + EqualityComparer>.Default.Equals(_logStore, logger._logStore); + } -// public override int GetHashCode() -// { -// return HashCode.Combine(_logStore); -// } -// } + public override int GetHashCode() + { + return HashCode.Combine(_logStore); + } + } -// } +} diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/LogEntry.cs b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/LogEntry.cs index 19317ca..98b9727 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/LogEntry.cs +++ b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/LogEntry.cs @@ -1,4 +1,4 @@ -/*using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -51,4 +51,3 @@ namespace TeamHobby.HobbyProjectGenerator.Logging } } -*/ \ No newline at end of file diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/LoggingManager.cs b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/LoggingManager.cs index f9c9d3a..b7148a9 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator.Logging/LoggingManager.cs +++ b/Source Code/TeamHobby.HobbyProjectGenerator.Logging/LoggingManager.cs @@ -1,4 +1,4 @@ -/*using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -7,6 +7,7 @@ using TeamHobby.HobbyProjectGenerator.DataAccess; using TeamHobby.HobbyProjectGenerator.Logging.Contracts; using TeamHobby.HobbyProjectGenerator.Logging.Implementations; + namespace TeamHobby.HobbyProjectGenerator.Logging { internal class LoggingManager @@ -42,4 +43,3 @@ namespace TeamHobby.HobbyProjectGenerator.Logging } } -*/ \ No newline at end of file diff --git a/Source Code/TeamHobby.HobbyProjectGenerator.UserManagement/TeamHobby.HobbyProjectGenerator.UserManagement.csproj b/Source Code/TeamHobby.HobbyProjectGenerator.UserManagement/TeamHobby.HobbyProjectGenerator.UserManagement.csproj index be6931d..a84e07e 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator.UserManagement/TeamHobby.HobbyProjectGenerator.UserManagement.csproj +++ b/Source Code/TeamHobby.HobbyProjectGenerator.UserManagement/TeamHobby.HobbyProjectGenerator.UserManagement.csproj @@ -7,7 +7,9 @@ + + diff --git a/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.Tests/ArchivingXUnitTest.cs b/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.Tests/ArchivingXUnitTest.cs new file mode 100644 index 0000000..186f3c4 --- /dev/null +++ b/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.Tests/ArchivingXUnitTest.cs @@ -0,0 +1,378 @@ +using System; +using System.Data.Odbc; +using System.Diagnostics; +using System.IO; +using System.Threading; +using TeamHobby.HobbyProjectGenerator.Archive; +using TeamHobby.HobbyProjectGenerator.DataAccess; +using Xunit; +using Xunit.Abstractions; + +namespace TeamHobby.Archiving.xTests + + +{ + public class ArchivingXUnitTest + { + string dbInfo = "DRIVER={MariaDB ODBC 3.1 Driver};" + + "SERVER=localhost;" + + "DATABASE=hobby;" + + "UID=root;" + + "PASSWORD=Teamhobby;" + + "OPTION=3"; + + ITestOutputHelper output; + public ArchivingXUnitTest(ITestOutputHelper output) + { + this.output = output; + } + + [Fact] + public void IsArchiveFolderCreated() + { + // Arrange + SqlDAO sqlDAO = new SqlDAO(dbInfo); + ArchiveManager archiveManager = new ArchiveManager(sqlDAO); + string folderPath = @"C:/HobbyArchive"; + bool expectedVal = true; + + // Act + archiveManager.CreateArchiveFolder(); + + + // Assert + bool actualVal = Directory.Exists(folderPath); + output.WriteLine("Actual value: {0}, Expected value: {1}", actualVal, expectedVal); + Assert.Equal(expectedVal, actualVal); + // Cleaning up directory after test + output.WriteLine("Cleaning up Folder and File used for testing... "); + try + { + Directory.Delete(folderPath, true); + } + catch + { + Console.WriteLine("Folder failed to be deleted"); + } + output.WriteLine("Cleaning up completed. "); + } + + [Fact] + public void IsFileNameCreated() + { + // Arrange + SqlDAO sqlDAO = new SqlDAO(dbInfo); + string foldPath = @"C:\HobbyArchive"; + string fileName = DateTime.Now.ToString("M_d_yyyy") + "_archive.csv"; + string expectedFilePath = System.IO.Path.Combine(foldPath, fileName); + ArchiveManager archiveManager = new ArchiveManager(sqlDAO); + + // Act + string actualFilePath = archiveManager.CreateOutFileName(); + + // Assert + + Assert.Equal(expectedFilePath, actualFilePath); + } + + [Fact] + public void IsCSVFileExist() + { + // Arrange + SqlDAO sqlDAO = new SqlDAO(dbInfo); + ArchiveManager archiveManager = new ArchiveManager(sqlDAO); + SqlDAO sqlDS = null; + string folderPath = @"C:/HobbyArchive"; + + try + { + bool folderRes = archiveManager.CreateArchiveFolder(); + } + catch (Exception ex) + { + Console.WriteLine("Folder creation for copying failed"); + } + string filePath = archiveManager.CreateOutFileName(); + bool expectedVal = true; + + if (sqlDAO.GetType() == typeof(SqlDAO)) + { + sqlDS = (SqlDAO)sqlDAO; + } + + // Act + if (sqlDS != null) + { + try + { + sqlDS.CopyToFile(filePath); + } + catch (Exception ex) + { + Console.WriteLine("Copying to a file failed !!"); + Console.WriteLine(ex.Message); + } + } + + // Assert + bool actualVal = File.Exists(filePath); + output.WriteLine("Actual value: {0}, Expected value: {1}", actualVal, expectedVal); + Assert.Equal(expectedVal, actualVal); + output.WriteLine("Cleaning up Folder and File used for testing... "); + try + { + Directory.Delete(folderPath, true); + } + catch + { + Console.WriteLine("Folder failed to be deleted"); + } + output.WriteLine("Cleaning up completed. "); + } + + [Fact] + public void IsEntriesRemoved() + { + // Arrange + SqlDAO sqlDAO = new SqlDAO(dbInfo); + ArchiveManager archiveManager = new ArchiveManager(sqlDAO); + OdbcDataReader odbcObj = null; + Object resultData = null; + bool expectedVal = false; + + sqlDAO.WriteData("INSERT into log(LtimeStamp, LvName, catname, userop, logmessage) values" + + "('2021-08-07 23:00:00', 'Info', 'View', 'create some projects', 'new account created')," + + "('2021-06-04 23:00:00', 'Info', 'Business', 'create some projects', 'new projects made')," + + "('2021-07-02 23:00:00', 'Info', 'View', 'log out', 'log out successful')," + + "('2021-09-03 23:00:00', 'Info', 'Business', 'log in', 'log in successfully')," + + "('2021-10-20 23:00:00', 'Info', 'View', 'search for projects', 'result return');"); + + // Act + sqlDAO.RemoveEntries(); + + // Assert + bool actualVal; + + resultData = sqlDAO.ReadData("SELECT * FROM log WHERE DATEDIFF(CURRENT_TIMESTAMP, log.LtimeStamp) > 30;"); + if ((resultData != null) && (resultData.GetType() == typeof(OdbcDataReader))) + { + odbcObj = (OdbcDataReader)resultData; + } + OdbcConnection conn = sqlDAO.GetConnection(); + actualVal = odbcObj.HasRows; + conn.Close(); + + output.WriteLine("Actual value: {0}, Expected value: {1}", actualVal, expectedVal); + Assert.Equal(expectedVal, actualVal); + + } + + + [Fact] + public void IsOutputFileRemoved() + { + // Arrange + SqlDAO sqlDAO = new SqlDAO(dbInfo); + ArchiveManager archiveManager = new ArchiveManager(sqlDAO); + bool expectedVal = false; + //Thread.Sleep(5000); + + // Create the folder and the csv file for compressing + archiveManager.CreateArchiveFolder(); + string filepath = archiveManager.CreateOutFileName(); + sqlDAO.CopyToFile(filepath); + + // Act + if (sqlDAO != null) + { + sqlDAO.RemoveOutputFile(filepath); + } + + // Assert + bool actualVal = File.Exists(filepath); + Assert.Equal(expectedVal, actualVal); + } + + [Fact] + public void IsFileCompressed() + { + // Arrange + SqlDAO sqlDAO = new SqlDAO(dbInfo); + ArchiveManager archiveManager = new ArchiveManager(sqlDAO); + + // Create the folder and the csv file to be compressed. + archiveManager.CreateArchiveFolder(); + string filePath = archiveManager.CreateOutFileName(); + sqlDAO.CopyToFile(filePath); + FileAttributes expectedAttribute = FileAttributes.Archive; + string compFilePath = Path.ChangeExtension(filePath, ".gz"); + + // Act + sqlDAO.CompressFile(filePath); + + // Assert + FileAttributes actualAttribute = File.GetAttributes(compFilePath); + output.WriteLine("Actual value: {0}, Expected value: {1}", actualAttribute, expectedAttribute); + Assert.Equal(expectedAttribute, actualAttribute); + + } + + [Fact] + public void IsArchive_Under_60s_10kRecords() + { + // Arrange + SqlDAO sqlDAO = new SqlDAO(dbInfo); + ArchiveManager archiveManager = new ArchiveManager(sqlDAO); + var timer = new Stopwatch(); + double expectedVal = 60; + string folderPath = @"C:/HobbyArchive"; + + sqlDAO.WriteData("INSERT into log(LtimeStamp, LvName, catname, userop, logmessage) values" + + "('2021-08-07 23:00:00', 'Info', 'View', 'create some projects', 'new account created')," + + "('2021-06-04 23:00:00', 'Info', 'Business', 'create some projects', 'new projects made')," + + "('2021-07-02 23:00:00', 'Info', 'View', 'log out', 'log out successful')," + + "('2021-09-03 23:00:00', 'Info', 'Business', 'log in', 'log in successfully')," + + "('2021-10-20 23:00:00', 'Info', 'View', 'search for projects', 'result return');"); + + for (int i = 0; i <= 11; i++) + { + sqlDAO.WriteData("INSERT INTO log(LtimeStamp, LvName, catName, userOP, logMessage) " + + "SELECT LtimeStamp, LvName, catName, userOP, logMessage FROM log WHERE DATEDIFF(CURRENT_TIMESTAMP, log.LtimeStamp) > 30;"); + } + + // Act + timer.Start(); + archiveManager.Controller(); + timer.Stop(); + + // Assert + var actualVal = timer.Elapsed.Seconds; + output.WriteLine("Actual value: {0}s, Expected value: {1}s", actualVal, expectedVal); + Assert.True(actualVal < expectedVal); + + // Clean up resources after testing + output.WriteLine("Cleaning up Folder and File used for testing... "); + try + { + Directory.Delete(folderPath, true); + } + catch + { + Console.WriteLine("Folder failed to be deleted"); + } + output.WriteLine("Cleaning up completed... "); + } + + [Fact] + public void IsArchive_Under_60s_1mRecords() + { + // Arrange + SqlDAO sqlDAO = new SqlDAO(dbInfo); + ArchiveManager archiveManager = new ArchiveManager(sqlDAO); + var timer = new Stopwatch(); + double expectedVal = 60; + string folderPath = @"C:/HobbyArchive"; + + sqlDAO.WriteData("INSERT into log(LtimeStamp, LvName, catname, userop, logmessage) values" + + "('2021-08-07 23:00:00', 'Info', 'View', 'create some projects', 'new account created')," + + "('2021-06-04 23:00:00', 'Info', 'Business', 'create some projects', 'new projects made')," + + "('2021-07-02 23:00:00', 'Info', 'View', 'log out', 'log out successful')," + + "('2021-09-03 23:00:00', 'Info', 'Business', 'log in', 'log in successfully')," + + "('2021-10-20 23:00:00', 'Info', 'View', 'search for projects', 'result return');"); + + for (int i = 0; i <= 18; i++) + { + sqlDAO.WriteData("INSERT INTO log(LtimeStamp, LvName, catName, userOP, logMessage) " + + "SELECT LtimeStamp, LvName, catName, userOP, logMessage FROM log WHERE DATEDIFF(CURRENT_TIMESTAMP, log.LtimeStamp) > 30;"); + } + + // Act + timer.Start(); + archiveManager.Controller(); + timer.Stop(); + + // Arrange + double actualVal = timer.Elapsed.Seconds; + output.WriteLine("Actual value: {0}s, Expected value: {1}s", actualVal, expectedVal); + Assert.True(actualVal < expectedVal); + + output.WriteLine("Cleaning up Folder and File used for testing... "); + //Clean up resources after testing + try + { + Directory.Delete(folderPath, true); + } + catch + { + Console.WriteLine("Folder failed to be deleted"); + } + output.WriteLine("Cleaning up completed... "); + } + + [Fact] + // [Test Method] + public void IsProcessFlowCompleted() + { + // Arrange + SqlDAO sqlDAO = new SqlDAO(dbInfo); + ArchiveManager archive = new ArchiveManager(sqlDAO); + bool expectedVal = true; + bool actualVal = false; + string folderPath = @"C:/HobbyArchive"; + + // Adding 10000 records to database for testing + sqlDAO.WriteData("INSERT into log(LtimeStamp, LvName, catname, userop, logmessage) values" + + "('2021-08-07 23:00:00', 'Info', 'View', 'create some projects', 'new account created')," + + "('2021-06-04 23:00:00', 'Info', 'Business', 'create some projects', 'new projects made')," + + "('2021-07-02 23:00:00', 'Info', 'View', 'log out', 'log out successful')," + + "('2021-09-03 23:00:00', 'Info', 'Business', 'log in', 'log in successfully')," + + "('2021-10-20 23:00:00', 'Info', 'View', 'search for projects', 'result return');"); + + for (int j = 0; j <= 11; j++) + { + sqlDAO.WriteData("INSERT INTO log(LtimeStamp, LvName, catName, userOP, logMessage) " + + "SELECT LtimeStamp, LvName, catName, userOP, logMessage FROM log WHERE DATEDIFF(CURRENT_TIMESTAMP, log.LtimeStamp) > 30;"); + } + + // Act + // Testing archive Manager + int i = -10; + while (i < 20) + { + DateTime date1 = new DateTime(2021, 12, 1, 0, 0, 0); + string currentTime = "00:00:00 AM"; + string currentDate = i.ToString(); + + if (i == 1) + { + currentDate = date1.ToString("dd"); + } + + //Console.WriteLine("Current date: {0}, Current Time: {1}", currentDate, currentTime); + Console.WriteLine("Current date: {0}, Current Time: {1}", currentDate, currentTime); + if (String.Equals(currentDate, "01") && String.Equals(currentTime, "00:00:00 AM")) + { + Console.WriteLine("Archiving process Start"); + actualVal = archive.Controller(); + } + + i++; + } + + // Assert + output.WriteLine("Actual value: {0}, Expected value: {1}", actualVal, expectedVal); + Assert.Equal(expectedVal, actualVal); + output.WriteLine("Cleaning up Folder and File used for testing... "); + try + { + Directory.Delete(folderPath, true); + } + catch + { + Console.WriteLine("Folder failed to be deleted"); + } + output.WriteLine("Cleaning up completed... "); + } + + } +} \ No newline at end of file diff --git a/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.Tests/TeamHobby.HobbyProjectGenerator.Tests.csproj b/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.Tests/TeamHobby.HobbyProjectGenerator.Tests.csproj new file mode 100644 index 0000000..55d4a91 --- /dev/null +++ b/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.Tests/TeamHobby.HobbyProjectGenerator.Tests.csproj @@ -0,0 +1,31 @@ + + + + net6.0 + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.UserManagement.xTests/UnitTest1.cs b/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.Tests/UseerManagementTests.cs similarity index 98% rename from Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.UserManagement.xTests/UnitTest1.cs rename to Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.Tests/UseerManagementTests.cs index 7f087bd..ec24f55 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.UserManagement.xTests/UnitTest1.cs +++ b/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.Tests/UseerManagementTests.cs @@ -8,11 +8,11 @@ using System.IO; namespace TeamHobby.UserManagement.xTests { - public class UnitTest1 + public class UseerManagementTests { ITestOutputHelper output; - public UnitTest1(ITestOutputHelper output) + public UseerManagementTests(ITestOutputHelper output) { this.output = output; } diff --git a/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.sln b/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.sln index 37a1f8a..a1de830 100644 --- a/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.sln +++ b/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.HobbyProjectGenerator.sln @@ -3,9 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31919.166 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamHobby.HobbyProjectGenerator.Models", "..\TeamHobby.HobbyProjectGenerator.Models\TeamHobby.HobbyProjectGenerator.Models.csproj", "{75DED6C2-D404-4E71-A58B-0F616DB5C062}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "main", "..\main\main.csproj", "{30C7EBF3-3957-46E5-86C1-C13356841ECA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamHobby.HobbyProjectGenerator.Main", "..\main\TeamHobby.HobbyProjectGenerator.Main.csproj", "{30C7EBF3-3957-46E5-86C1-C13356841ECA}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamHobby.HobbyProjectGenerator.Archive", "..\TeamHobby.HobbyProjectGenerator.Archive\TeamHobby.HobbyProjectGenerator.Archive.csproj", "{B88ED0D9-72E2-4245-BD8F-856FF42E500C}" EndProject @@ -13,21 +11,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamHobby.HobbyProjectGener EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamHobby.HobbyProjectGenerator.UserManagement", "..\TeamHobby.HobbyProjectGenerator.UserManagement\TeamHobby.HobbyProjectGenerator.UserManagement.csproj", "{2E7193B8-86B6-48DA-9671-CD84615A5F5D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamHobby.HobbyProjectGenerator.Logging.Tests", "..\TeamHobby.HobbyProjectGenerator.Logging.Tests\TeamHobby.HobbyProjectGenerator.Logging.Tests.csproj", "{CA8D11CF-807C-4C90-A529-0371F73518F6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamHobby.HobbyProjectGenerator.ArchiveTests", "..\TeamHobby.HobbyProjectGenerator.ArchiveTests\TeamHobby.HobbyProjectGenerator.ArchiveTests.csproj", "{C5EBD1F8-C806-4BF9-B2D7-8876072630FD}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamHobby.UserManagement.xTests", "TeamHobby.UserManagement.xTests\TeamHobby.UserManagement.xTests.csproj", "{6D575AF1-C138-44C5-B701-5AEC4ACEAA7A}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D29D9225-3748-4067-AF07-E677A525EF39}" ProjectSection(SolutionItems) = preProject TeamHobby.HobbyProjectGenerator.csproj = TeamHobby.HobbyProjectGenerator.csproj EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamHobby.Archiving.xTests", "TeamHobby.Archiving.xTests\TeamHobby.Archiving.xTests.csproj", "{8C039F49-13D3-4AEE-A1C3-A880751852BB}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeamHobby.HobbyProjectGenerator.Logging", "..\TeamHobby.HobbyProjectGenerator.Logging\TeamHobby.HobbyProjectGenerator.Logging.csproj", "{CA539CBE-A043-4ED8-9E1F-3E949F0A482D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamHobby.HobbyProjectGenerator.Tests", "TeamHobby.HobbyProjectGenerator.Tests\TeamHobby.HobbyProjectGenerator.Tests.csproj", "{2E9DC0A0-D9A1-40A5-9684-ECC8EDCD8DAD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -36,12 +28,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {75DED6C2-D404-4E71-A58B-0F616DB5C062}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {75DED6C2-D404-4E71-A58B-0F616DB5C062}.Debug|Any CPU.Build.0 = Debug|Any CPU - {75DED6C2-D404-4E71-A58B-0F616DB5C062}.Debug|x86.ActiveCfg = Debug|Any CPU - {75DED6C2-D404-4E71-A58B-0F616DB5C062}.Release|Any CPU.ActiveCfg = Release|Any CPU - {75DED6C2-D404-4E71-A58B-0F616DB5C062}.Release|Any CPU.Build.0 = Release|Any CPU - {75DED6C2-D404-4E71-A58B-0F616DB5C062}.Release|x86.ActiveCfg = Release|Any CPU {30C7EBF3-3957-46E5-86C1-C13356841ECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {30C7EBF3-3957-46E5-86C1-C13356841ECA}.Debug|Any CPU.Build.0 = Debug|Any CPU {30C7EBF3-3957-46E5-86C1-C13356841ECA}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -74,36 +60,6 @@ Global {2E7193B8-86B6-48DA-9671-CD84615A5F5D}.Release|Any CPU.Build.0 = Release|Any CPU {2E7193B8-86B6-48DA-9671-CD84615A5F5D}.Release|x86.ActiveCfg = Release|Any CPU {2E7193B8-86B6-48DA-9671-CD84615A5F5D}.Release|x86.Build.0 = Release|Any CPU - {CA8D11CF-807C-4C90-A529-0371F73518F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CA8D11CF-807C-4C90-A529-0371F73518F6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CA8D11CF-807C-4C90-A529-0371F73518F6}.Debug|x86.ActiveCfg = Debug|Any CPU - {CA8D11CF-807C-4C90-A529-0371F73518F6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CA8D11CF-807C-4C90-A529-0371F73518F6}.Release|Any CPU.Build.0 = Release|Any CPU - {CA8D11CF-807C-4C90-A529-0371F73518F6}.Release|x86.ActiveCfg = Release|Any CPU - {C5EBD1F8-C806-4BF9-B2D7-8876072630FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C5EBD1F8-C806-4BF9-B2D7-8876072630FD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C5EBD1F8-C806-4BF9-B2D7-8876072630FD}.Debug|x86.ActiveCfg = Debug|Any CPU - {C5EBD1F8-C806-4BF9-B2D7-8876072630FD}.Debug|x86.Build.0 = Debug|Any CPU - {C5EBD1F8-C806-4BF9-B2D7-8876072630FD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C5EBD1F8-C806-4BF9-B2D7-8876072630FD}.Release|Any CPU.Build.0 = Release|Any CPU - {C5EBD1F8-C806-4BF9-B2D7-8876072630FD}.Release|x86.ActiveCfg = Release|Any CPU - {C5EBD1F8-C806-4BF9-B2D7-8876072630FD}.Release|x86.Build.0 = Release|Any CPU - {6D575AF1-C138-44C5-B701-5AEC4ACEAA7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6D575AF1-C138-44C5-B701-5AEC4ACEAA7A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6D575AF1-C138-44C5-B701-5AEC4ACEAA7A}.Debug|x86.ActiveCfg = Debug|Any CPU - {6D575AF1-C138-44C5-B701-5AEC4ACEAA7A}.Debug|x86.Build.0 = Debug|Any CPU - {6D575AF1-C138-44C5-B701-5AEC4ACEAA7A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6D575AF1-C138-44C5-B701-5AEC4ACEAA7A}.Release|Any CPU.Build.0 = Release|Any CPU - {6D575AF1-C138-44C5-B701-5AEC4ACEAA7A}.Release|x86.ActiveCfg = Release|Any CPU - {6D575AF1-C138-44C5-B701-5AEC4ACEAA7A}.Release|x86.Build.0 = Release|Any CPU - {8C039F49-13D3-4AEE-A1C3-A880751852BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8C039F49-13D3-4AEE-A1C3-A880751852BB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C039F49-13D3-4AEE-A1C3-A880751852BB}.Debug|x86.ActiveCfg = Debug|Any CPU - {8C039F49-13D3-4AEE-A1C3-A880751852BB}.Debug|x86.Build.0 = Debug|Any CPU - {8C039F49-13D3-4AEE-A1C3-A880751852BB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8C039F49-13D3-4AEE-A1C3-A880751852BB}.Release|Any CPU.Build.0 = Release|Any CPU - {8C039F49-13D3-4AEE-A1C3-A880751852BB}.Release|x86.ActiveCfg = Release|Any CPU - {8C039F49-13D3-4AEE-A1C3-A880751852BB}.Release|x86.Build.0 = Release|Any CPU {CA539CBE-A043-4ED8-9E1F-3E949F0A482D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CA539CBE-A043-4ED8-9E1F-3E949F0A482D}.Debug|Any CPU.Build.0 = Debug|Any CPU {CA539CBE-A043-4ED8-9E1F-3E949F0A482D}.Debug|x86.ActiveCfg = Debug|x86 @@ -112,6 +68,14 @@ Global {CA539CBE-A043-4ED8-9E1F-3E949F0A482D}.Release|Any CPU.Build.0 = Release|Any CPU {CA539CBE-A043-4ED8-9E1F-3E949F0A482D}.Release|x86.ActiveCfg = Release|x86 {CA539CBE-A043-4ED8-9E1F-3E949F0A482D}.Release|x86.Build.0 = Release|x86 + {2E9DC0A0-D9A1-40A5-9684-ECC8EDCD8DAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E9DC0A0-D9A1-40A5-9684-ECC8EDCD8DAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E9DC0A0-D9A1-40A5-9684-ECC8EDCD8DAD}.Debug|x86.ActiveCfg = Debug|Any CPU + {2E9DC0A0-D9A1-40A5-9684-ECC8EDCD8DAD}.Debug|x86.Build.0 = Debug|Any CPU + {2E9DC0A0-D9A1-40A5-9684-ECC8EDCD8DAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E9DC0A0-D9A1-40A5-9684-ECC8EDCD8DAD}.Release|Any CPU.Build.0 = Release|Any CPU + {2E9DC0A0-D9A1-40A5-9684-ECC8EDCD8DAD}.Release|x86.ActiveCfg = Release|Any CPU + {2E9DC0A0-D9A1-40A5-9684-ECC8EDCD8DAD}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.UserManagement.xTests/UseerManagementTests.cs b/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.UserManagement.xTests/UseerManagementTests.cs new file mode 100644 index 0000000..ec24f55 --- /dev/null +++ b/Source Code/TeamHobby.HobbyProjectGenerator/TeamHobby.UserManagement.xTests/UseerManagementTests.cs @@ -0,0 +1,264 @@ +using System; +using Xunit; +using Xunit.Abstractions; +using System.Threading; +using TeamHobby.HobbyProjectGenerator.UserManagement; +using TeamHobby.HobbyProjectGenerator.DataAccess; +using System.IO; + +namespace TeamHobby.UserManagement.xTests +{ + public class UseerManagementTests + { + + ITestOutputHelper output; + public UseerManagementTests(ITestOutputHelper output) + { + this.output = output; + } + // User is able to perform any single UM operation within 5 seconds upon invocation.A system message displays “UM operation was successful” + [Fact] + public void SingleOperationinFiveSec() + { + // Arrange + DateTime sTime = DateTime.Now; + var TestAcc = new UserAccount("newUser", "4567", "email@a.com", "regular", sTime); + var serviceTest = new AccountService(); + string dbType = "sql"; + RDSFactory dbFactory = new RDSFactory(); + + // Testing Data Access Layer + string dbInfo = "DRIVER={MariaDB ODBC 3.1 Driver};" + + "TCPIP=1;" + + "SERVER=localhost;" + + "DATABASE=hobby;" + + "UID=root;" + + "PASSWORD=Teamhobby;" + + "OPTION=3"; + IDataSource datasource = dbFactory.getDataSource(dbType, dbInfo); + + // Act + // Create a file + serviceTest.CreateUserRecord(TestAcc, "Rifat", datasource); + DateTime eTime = DateTime.Now; + TimeSpan timeDiff = (eTime - sTime); + var sec = timeDiff.TotalSeconds; + + // Assert + if (sec > 5) + { + output.WriteLine("Single Operation was unsuccessful"); + Assert.True(false); + } + else + { + Assert.True(true); + output.WriteLine("Single Operation was successful"); + } + } + + // Single UM operation takes longer than 5 seconds + [Fact] + public void FailSingleOperationinFiveSec() + { + // Arrange + DateTime sTime = DateTime.Now; + var TestAcc = new UserAccount("newUser", "4567", "email@a.com", "regular", sTime); + var serviceTest = new AccountService(); + string dbType = "sql"; + RDSFactory dbFactory = new RDSFactory(); + + // Testing Data Access Layer + string dbInfo = "DRIVER={MariaDB ODBC 3.1 Driver};" + + "TCPIP=1;" + + "SERVER=localhost;" + + "DATABASE=hobby;" + + "UID=root;" + + "PASSWORD=Teamhobby;" + + "OPTION=3"; + IDataSource datasource = dbFactory.getDataSource(dbType, dbInfo); + + // Act + // Create a file + serviceTest.CreateUserRecord(TestAcc, "Rifat", datasource); + Thread.Sleep(6000); + DateTime eTime = DateTime.Now; + TimeSpan timeDiff = (eTime - sTime); + var sec = timeDiff.TotalSeconds; + + // Assert + if (sec > 5) + { + output.WriteLine("Single Operation was unsuccessful"); + Assert.False(false); + } + else + { + Assert.True(false); + output.WriteLine("Single Operation was successful"); + } + } + // Single UM operation completes within 5 seconds, but no system message is shown or inaccurate system message is shown + [Fact] + public void SingleOperationwithMessage() + { + // Arrange + DateTime sTime = DateTime.Now; + var TestAcc = new UserAccount("newUserMessage", "4567", "email@a.com", "regular", sTime); + var serviceTest = new AccountService(); + string dbType = "sql"; + RDSFactory dbFactory = new RDSFactory(); + var checkMess = false; + // Testing Data Access Layer + string dbInfo = "DRIVER={MariaDB ODBC 3.1 Driver};" + + "TCPIP=1;" + + "SERVER=localhost;" + + "DATABASE=hobby;" + + "UID=root;" + + "PASSWORD=Teamhobby;" + + "OPTION=3"; + IDataSource datasource = dbFactory.getDataSource(dbType, dbInfo); + + // Act + // Create a file + serviceTest.CreateUserRecord(TestAcc, "Rifat", datasource); + DateTime eTime = DateTime.Now; + TimeSpan timeDiff = (eTime - sTime); + var sec = timeDiff.TotalSeconds; + + // Assert + if (sec > 5) + { + var message = "Single Operation was unsuccessful"; + Assert.False(true); + output.WriteLine(message); + if (checkMess) + { + Assert.False(true); + throw new Exception("Message was not printed"); + } + } + else + { + Assert.True(true); + var message2 = "Single Operation was successful"; + output.WriteLine(message2); + if (!checkMess) + { + output.WriteLine("Message was printed"); + Assert.True(true); + } + } + } + // Bulk UM operations completes within 60 seconds, but no system message is shown or inaccurate system message is shown + [Fact] + public void BulkOperationwithMessage() + { + // Arrange + DateTime sTime = DateTime.Now; + var checkMess = false; + var serviceTest = new AccountService(); + string dbType = "sql"; + RDSFactory dbFactory = new RDSFactory(); + string path = Directory.GetCurrentDirectory(); + + + // Testing Data Access Layer + string dbInfo = "DRIVER={MariaDB ODBC 3.1 Driver};" + + "TCPIP=1;" + + "SERVER=localhost;" + + "DATABASE=hobby;" + + "UID=root;" + + "PASSWORD=Teamhobby;" + + "OPTION=3"; + IDataSource datasource = dbFactory.getDataSource(dbType, dbInfo); + + // Act + // Create a file + //for (int i = 0; i < 10000; i++) + //{ + //var TestAcc = new UserAccount("newUser" + $"{i}", "4567", $"email{i}@a.com", "regular", sTime); + serviceTest.BulkOperation("Rifat", path + "\\Bulk.txt", datasource); + //} + + DateTime eTime = DateTime.Now; + TimeSpan timeDiff = (eTime - sTime); + var sec = timeDiff.TotalSeconds; + + + // Assert + if (sec > 60) + { + var message = "Bulk Operation was unsuccessful"; + Assert.True(false); + output.WriteLine(message); + if (checkMess) + { + Assert.True(false); + throw new Exception("Message was not printed"); + } + } + else + { + Assert.True(true); + var message2 = "Bulk Operation was successful"; + output.WriteLine(message2); + if (!checkMess) + { + output.WriteLine("Message was printed"); + Assert.True(true); + } + } + } + // User is able to perform less than 10K UM operations in bulk within 60 seconds. A system message displays “Bulk UM operation was successful” + [Fact] + public void BulkOperationforSixty() + { + // Arrange + DateTime sTime = DateTime.Now; + + var serviceTest = new AccountService(); + string dbType = "sql"; + RDSFactory dbFactory = new RDSFactory(); + string dir = Directory.GetCurrentDirectory(); + string path = dir + "\\Bulk.txt"; + output.WriteLine(path); + + + // Testing Data Access Layer + string dbInfo = "DRIVER={MariaDB ODBC 3.1 Driver};" + + "TCPIP=1;" + + "SERVER=localhost;" + + "DATABASE=hobby;" + + "UID=root;" + + "PASSWORD=Teamhobby;" + + "OPTION=3"; + IDataSource datasource = dbFactory.getDataSource(dbType, dbInfo); + + // Act + // Create a file + //for (int i = 0; i < 10000; i++) + //{ + //var TestAcc = new UserAccount("newUser" + $"{i}", "4567", $"email{i}@a.com", "regular", sTime); + serviceTest.BulkOperation("Rifat", path, datasource); + //} + + DateTime eTime = DateTime.Now; + TimeSpan timeDiff = (eTime - sTime); + var sec = timeDiff.TotalSeconds; + + + // Assert + if (sec > 60) + { + output.WriteLine("Bulk Operation was unsuccessful"); + Assert.True(false); + } + else + { + Assert.True(true); + output.WriteLine("Bulk Operation was successful"); + } + } + } +} \ No newline at end of file diff --git a/Source Code/TeamHobby.Main/HobbyMain.cs b/Source Code/TeamHobby.Main/HobbyMain.cs deleted file mode 100644 index 58e9b09..0000000 --- a/Source Code/TeamHobby.Main/HobbyMain.cs +++ /dev/null @@ -1,148 +0,0 @@ -// See https://aka.ms/new-console-template for more information -//using System.Data.SqlClient; -using System.IO.Compression; -using TeamHobby.HobbyProjectGenerator.Archive; -//using MySql.Data.MySqlClient; -using System.Data.Odbc; - -public class HobbyMain -{ - static void Main(string[] args) - { - // Testing file compression Start - //Console.WriteLine("Hello World!"); - - //// Testing Compressing a file - //string fileName = @"C:\Users\Chunchunmaru\Documents\csulbFall2021\HobbyProject\Source Code\TeamHobby.Main\rando"; - ////string fileName = @"arhiving2.txt"; - //FileInfo fileInfo = new FileInfo(fileName); - - //Console.WriteLine("File Name: {0}", fileInfo.FullName); - - //SQLSource sqlSource = new SQLSource(); - //bool res = sqlSource.CompressFile(fileName); - ////bool res = CompressFile(fileName); - //Console.WriteLine(res); - - //Console.WriteLine(CreateFileName()); - - // Testing File compression end - - // testing end - - // Testing connection to MariaDB - - string connString = "user id=root;" + "password=Teamhobby;server=localhost;" + "Trusted_Connection=yes;" + "database=Alatreon; " + "connection timeout=5"; - string connMariaDB = "server=localhost;port=3306;uid=root;pwd=Teamhobby;connection timeout=5"; - //var conn = new SqlConnection(connString); - - - - - //// SqlConnection conn = new SqlConnection(connString); - string sqlQ = "Select * from log;"; - - //MySql.Data.MySqlClient.MySqlConnection connect; - - //try - //{ - // Console.WriteLine("Open connection: "); - // //connect = new MySql.Data.MySqlClient.MySqlConnection(); - // //connect.ConnectionString = connMariaDB; - // //connect.Open(); - // conn.Open(); - - // SqlCommand cmd = new SqlCommand(sqlQ, conn); - - // SqlDataReader myReader = cmd.ExecuteReader(); - // while (myReader.Read()) - // { - // Console.WriteLine(myReader["Column1"].ToString()); - // Console.WriteLine(myReader["Column2"].ToString()); - // } - // conn.Close(); - - // //if (connect.State == System.Data.ConnectionState.Open) - // //{ - // // Console.WriteLine("Connection established"); - // //} - // //connect.Close(); - - //} - try - { - Console.WriteLine("Opening connection: "); - //Connection string for Connector/ODBC 3.51 - string MyConString = "DRIVER={MariaDB ODBC 3.1 Driver};" + - "SERVER=localhost;" + - "DATABASE=hobby;" + - "UID=root;" + - "PASSWORD=Teamhobby;" + - "OPTION=3"; - - //Connect to MySQL using Connector/ODBC - //OdbcConnection MyConnection = new OdbcConnection(MyConString); - //MyConnection.Open(); - - //Console.WriteLine("\n !!! success, connected successfully !!!\n"); - - ////Display connection information - //Console.WriteLine("Connection Information:"); - //Console.WriteLine("\tConnection String:" + - // MyConnection.ConnectionString); - //Console.WriteLine("\tConnection Timeout:" + - // MyConnection.ConnectionTimeout); - //Console.WriteLine("\tDatabase:" + - // MyConnection.Database); - //Console.WriteLine("\tDataSource:" + - // MyConnection.DataSource); - //Console.WriteLine("\tDriver:" + - // MyConnection.Driver); - //Console.WriteLine("\tServerVersion:" + - // MyConnection.ServerVersion); - - //Console.WriteLine("Connection Successful"); - - ReadData(MyConString); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - Console.WriteLine("COnnection failed"); - - } - } - - public static void ReadData(string connectionString) - { - string queryString = "SELECT * from log;"; - - using (OdbcConnection connection = new OdbcConnection(connectionString)) - { - OdbcCommand command = new OdbcCommand(queryString, connection); - - connection.Open(); - - // Execute the DataReader and access the data. - OdbcDataReader reader = command.ExecuteReader(); - Console.WriteLine("Read the database"); - while (reader.Read()) - { - Console.WriteLine("Date={0} {1} {2} {3} {4} {5}", reader[0], reader[1], reader[2], reader[3], reader[4], reader[5]); - //Console.WriteLine("Col A: {0} ", reader[0]); - //Console.WriteLine("Column: " + reader.FieldCount); - //Console.WriteLine("Column={1}", reader[1]); - - } - - // Call Close when done reading. - reader.Close(); - connection.Close(); - } - } - - public static string CreateFileName() - { - return DateTime.Now.ToString() + "archive.txt"; - } -} diff --git a/Source Code/TeamHobby.Main/MasterController.cs b/Source Code/TeamHobby.Main/MasterController.cs deleted file mode 100644 index 838f386..0000000 --- a/Source Code/TeamHobby.Main/MasterController.cs +++ /dev/null @@ -1,37 +0,0 @@ - -public class MasterController -{ - - public static void main(string[] args) - { - - //The main Controller of the program, It will run all services here - - // While loop to run everything - - // 1. Initialize the Data Factory here - - // 2. Create the SqlConnection object - - int userInput = 0; - - while (userInput != -1) - { - - // Print the menu - - // - if (userInput == 1) - { - // 1. User Managment goes here - } - - // Trigger Archiving automatically - // Create new ArchiveManager object - // if (currentDate == 1){ - // ArchiveManager.Controller() - - // Logging also automatic - } - } -} \ No newline at end of file diff --git a/Source Code/TeamHobby.Main/TeamHobby.Main.csproj b/Source Code/TeamHobby.Main/TeamHobby.Main.csproj deleted file mode 100644 index 9791008..0000000 --- a/Source Code/TeamHobby.Main/TeamHobby.Main.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - Exe - net6.0 - enable - enable - HobbyMain - - - - - - - - - - - - diff --git a/Source Code/TeamHobby.Main/archiving2.txt b/Source Code/TeamHobby.Main/archiving2.txt deleted file mode 100644 index a2ba694..0000000 --- a/Source Code/TeamHobby.Main/archiving2.txt +++ /dev/null @@ -1,46 +0,0 @@ -title Use Case: Archiving - -participant ArchiveController.cs -participant DataConnection -participant DataSourceFactory -participant SQLSource -participant MariaDB - -activate ArchiveController.cs -activate DataConnection -ArchiveController.cs ->DataConnection:static DataConnection getDataConnection() -DataConnection -->ArchiveController.cs:return DataConnection -deactivate DataConnection - -activate DataSourceFactory -ArchiveController.cs ->DataSourceFactory: new DataSourceFactory() -DataSourceFactory ->DataSourceFactory:DataSourceFactory()\nconstructor -ArchiveController.cs <-- DataSourceFactory:return DataSourceFactory -deactivate DataSourceFactory - -activate SQLSource -ArchiveController.cs->SQLSource: IDataSource DataSourceFact.GetDataSource(String sourceName, string info) -SQLSource->SQLSource: SQLSource(string info)\nconstructor -SQLSource --> ArchiveController.cs :return SQLSource - -loop #lightblue while true -alt #lightgreen date == 1 -ArchiveController.cs ->ArchiveController.cs: string fileName = string CreateFileName( ) -ArchiveController.cs -> SQLSource: int Compress(string fileName) -activate MariaDB - -SQLSource -> MariaDB: int createArchive(string fileLocation) -MariaDB -->SQLSource:return 0 - -SQLSource ->MariaDB: int RemoveEntries() -MariaDB-->SQLSource:return 0 -end - - -deactivate MariaDB - -SQLSource-->ArchiveController.cs:return 0 -end - -deactivate ArchiveController.cs -deactivate SQLSource \ No newline at end of file diff --git a/Source Code/TeamHobby.Main/rando b/Source Code/TeamHobby.Main/rando deleted file mode 100644 index a2ba694..0000000 --- a/Source Code/TeamHobby.Main/rando +++ /dev/null @@ -1,46 +0,0 @@ -title Use Case: Archiving - -participant ArchiveController.cs -participant DataConnection -participant DataSourceFactory -participant SQLSource -participant MariaDB - -activate ArchiveController.cs -activate DataConnection -ArchiveController.cs ->DataConnection:static DataConnection getDataConnection() -DataConnection -->ArchiveController.cs:return DataConnection -deactivate DataConnection - -activate DataSourceFactory -ArchiveController.cs ->DataSourceFactory: new DataSourceFactory() -DataSourceFactory ->DataSourceFactory:DataSourceFactory()\nconstructor -ArchiveController.cs <-- DataSourceFactory:return DataSourceFactory -deactivate DataSourceFactory - -activate SQLSource -ArchiveController.cs->SQLSource: IDataSource DataSourceFact.GetDataSource(String sourceName, string info) -SQLSource->SQLSource: SQLSource(string info)\nconstructor -SQLSource --> ArchiveController.cs :return SQLSource - -loop #lightblue while true -alt #lightgreen date == 1 -ArchiveController.cs ->ArchiveController.cs: string fileName = string CreateFileName( ) -ArchiveController.cs -> SQLSource: int Compress(string fileName) -activate MariaDB - -SQLSource -> MariaDB: int createArchive(string fileLocation) -MariaDB -->SQLSource:return 0 - -SQLSource ->MariaDB: int RemoveEntries() -MariaDB-->SQLSource:return 0 -end - - -deactivate MariaDB - -SQLSource-->ArchiveController.cs:return 0 -end - -deactivate ArchiveController.cs -deactivate SQLSource \ No newline at end of file diff --git a/Source Code/TeamHobby.Main/text.cs b/Source Code/TeamHobby.Main/text.cs deleted file mode 100644 index 85362c8..0000000 --- a/Source Code/TeamHobby.Main/text.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace TeamHobby.Main -{ - internal class text - { - } -} diff --git a/Source Code/main/main.csproj b/Source Code/main/TeamHobby.HobbyProjectGenerator.Main.csproj similarity index 87% rename from Source Code/main/main.csproj rename to Source Code/main/TeamHobby.HobbyProjectGenerator.Main.csproj index f8ad87e..37d691c 100644 --- a/Source Code/main/main.csproj +++ b/Source Code/main/TeamHobby.HobbyProjectGenerator.Main.csproj @@ -16,6 +16,7 @@ +