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
93 changes: 93 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Visual Studio
.vs/
*.suo
*.user
*.userprefs
*.log
*.pdb
*.pidb
*.vshost.*
*.testsettings
*.trx
*.webtest
*.cache
*.obj
*.bak
*.resharper
*.dotSettings
*.resharper.user
*.resharper.host
*.csproj.user
*.vcxproj.user
*.sln.docstates
*.suo
*.user
*.userprefs
*.log
*.pdb
*.pidb
*.vshost.*
*.testsettings
*.trx
*.webtest
*.cache
*.obj
*.bak
*.resharper
*.dotSettings
*.resharper.user
*.resharper.host
*.csproj.user
*.vcxproj.user
*.sln.docstates

# Build artifacts
[Bb]in/
[Oo]bj/
[Dd]ebug/
[Rr]elease/
*.dll
*.exe
*.exp
*.lib
*.ilk
*.idb
*.winmd
*.appx
*.msi
*.msp
*.pri
*.sgen
*.xap
*.zip
*.nupkg
*.publish
*.deploy
_PublishedWebsites/
app.publish/
TestResults/

# NuGet packages
packages/
*.nuget.props
*.nuget.targets

# Node.js (if used for front-end or build tools)
node_modules/
npm-debug.log

# Rider
.idea/

# Visual Studio Code
.vscode/

# Environment-specific files
appsettings.*.json # Exclude sensitive appsettings files, but keep appsettings.json
*.json.user
*.env
.env.*

# Miscellaneous
Thumbs.db
Desktop.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using InvestmentPerformanceAPI;
using InvestmentPerformanceAPI.Models;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

namespace InvestmentPerformanceAPI.IntegrationTests
{
public class UsersControllerTests
{
private WebApplicationFactory<Program> _factory;
private HttpClient _client;

[OneTimeSetUp]
public void Setup()
{
_factory = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
services.Remove(
services.SingleOrDefault(d => d.ServiceType == typeof(IDbContextOptionsConfiguration<AppDbContext>))
);

services.AddDbContext<AppDbContext>(options =>
options.UseInMemoryDatabase("IntegrationTestsDb"));

var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
context.Database.EnsureCreated();

context.Users.Add(new User
{
Id = 1,
Name = "Rudy",
Investments = new List<Investment>
{
new Investment { Id = 1, Name = "QQQ", CostBasisPerShare = 40, CurrentPrice = 70, NumberOfShares = 1, CurrentValue = 70 },
new Investment { Id = 2, Name = "SPY", CostBasisPerShare = 50, CurrentPrice = 80, NumberOfShares = 2, CurrentValue = 160 }
}
});
context.SaveChanges();
});
});

_client = _factory.CreateClient();
}

[OneTimeTearDown]
public void TearDown()
{
_factory.Dispose();
_client.Dispose();
}

[Test]
public async Task GetUserInvestments_ReturnsOk_WhenUserExists()
{
var response = await _client.GetAsync("/user/1/investments");
response.EnsureSuccessStatusCode();

var json = await response.Content.ReadAsStringAsync();
Assert.That(json.Contains("QQQ"));
Assert.That(json.Contains("SPY"));
}

[Test]
public async Task GetUserInvestments_ReturnsNotFound_WhenUserDoesNotExist()
{
var response = await _client.GetAsync("user/99/investments");
Assert.That(response.StatusCode, Is.EqualTo(System.Net.HttpStatusCode.NotFound));
}

[Test]
public async Task GetUserInvestmentDetails_ReturnsOk_WhenUserExists()
{
var response = await _client.GetAsync("/user/1/investment/2");
response.EnsureSuccessStatusCode();

var json = await response.Content.ReadAsStringAsync();
Assert.That(json.Contains("SPY"));
}

[Test]
public async Task GetUserInvestmentDetails_ReturnsNotFound_WhenUserDoesNotExist()
{
var response = await _client.GetAsync("user/99/investment/2");
Assert.That(response.StatusCode, Is.EqualTo(System.Net.HttpStatusCode.NotFound));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.11" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\InvestmentPerformanceAPI\InvestmentPerformanceAPI.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>

</Project>
31 changes: 31 additions & 0 deletions InvestmentPerformanceAPI/InvestmentPerformanceAPI.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36705.20
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InvestmentPerformanceAPI", "InvestmentPerformanceAPI\InvestmentPerformanceAPI.csproj", "{58D4DC25-A7FF-499C-9985-CFF8F3A3E409}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InvestmentPerformanceAPI.IntegrationTests", "InvestmentPerformanceAPI.IntegrationTests\InvestmentPerformanceAPI.IntegrationTests.csproj", "{131612DC-1D45-D8C9-7123-FA23DF84F028}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{58D4DC25-A7FF-499C-9985-CFF8F3A3E409}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{58D4DC25-A7FF-499C-9985-CFF8F3A3E409}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58D4DC25-A7FF-499C-9985-CFF8F3A3E409}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58D4DC25-A7FF-499C-9985-CFF8F3A3E409}.Release|Any CPU.Build.0 = Release|Any CPU
{131612DC-1D45-D8C9-7123-FA23DF84F028}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{131612DC-1D45-D8C9-7123-FA23DF84F028}.Debug|Any CPU.Build.0 = Debug|Any CPU
{131612DC-1D45-D8C9-7123-FA23DF84F028}.Release|Any CPU.ActiveCfg = Release|Any CPU
{131612DC-1D45-D8C9-7123-FA23DF84F028}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AEB35FDF-1ACA-495F-BCDC-7E80009BD3C7}
EndGlobalSection
EndGlobal
18 changes: 18 additions & 0 deletions InvestmentPerformanceAPI/InvestmentPerformanceAPI/AppDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using InvestmentPerformanceAPI.Models;
using Microsoft.EntityFrameworkCore;

namespace InvestmentPerformanceAPI
{
public class AppDbContext : DbContext, IAppDbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

public DbSet<User> Users { get; set; }
public DbSet<Investment> Investments { get; set; }

public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
return base.SaveChangesAsync(cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using InvestmentPerformanceAPI.DTOs;
using InvestmentPerformanceAPI.Models;
using InvestmentPerformanceAPI.Services;
using Microsoft.AspNetCore.Mvc;

namespace InvestmentPerformanceAPI.Controllers
{
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{
private readonly ILogger<UserController> _logger;
private readonly IUserInvestmentService _userInvestmentService;

public UserController(ILogger<UserController> logger, IUserInvestmentService userInvestmentService)
{
_logger = logger;
_userInvestmentService = userInvestmentService;
}

//get endpoint exposing a given users investments
[HttpGet("{userId}/investments")]
public async Task<IActionResult> GetUserInvestments(int userId)
{
_logger.LogInformation("GET: /" + userId + "/investments");
var investments = await _userInvestmentService.GetUserInvestments(userId);

if (investments == null)
{
_logger.LogInformation($"User with ID {userId} not found.");
return NotFound($"User with ID {userId} not found.");
}

return Ok(investments);
}

//get endpoint exposing a users certain investment details
[HttpGet("{userId}/investment/{investmentId}")]
public async Task<IActionResult> GetUserInvestments(int userId, int investmentId)
{
_logger.LogInformation("GET: /" + userId + "/investment/" + investmentId);
var investment = await _userInvestmentService.GetUserInvestmentDetails(userId, investmentId);

if (investment == null)
{
_logger.LogInformation($"Investment with ID {investmentId} not found.");
return NotFound($"Investment with ID {investmentId} not found.");
}

return Ok(investment);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using InvestmentPerformanceAPI.Enums;
using InvestmentPerformanceAPI.Models;

namespace InvestmentPerformanceAPI.DTOs
{
public class InvestmentDTO
{
public int Id { get; set; }
public required string Name { get; set; }
public decimal CostBasisPerShare { get; set; }
public decimal CurrentValue { get; set; }
public decimal CurrentPrice { get; set; }
public string? Term { get; set; }
public decimal TotalGains { get; set; }
public decimal NumberOfShares { get; set; }

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using InvestmentPerformanceAPI.Models;

namespace InvestmentPerformanceAPI.DTOs
{
public class UserInvestmentDTO
{
public required int InvestmentId { get; set; } = default;
public required string InvestmentName { get; set; }

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace InvestmentPerformanceAPI.Enums
{
public enum TermEnum
{
Short = 0,
Long = 1,
}
}
13 changes: 13 additions & 0 deletions InvestmentPerformanceAPI/InvestmentPerformanceAPI/IAppDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using InvestmentPerformanceAPI.Models;
using Microsoft.EntityFrameworkCore;

namespace InvestmentPerformanceAPI
{
public interface IAppDbContext
{
DbSet<User> Users { get; set; }
DbSet<Investment> Investments { get; set; }

Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}
}
Loading