diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 77c8e2b5f..dd59c5066 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,6 @@ # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.187.0/containers/dotnet/.devcontainer/base.Dockerfile -ARG VARIANT="5.0" +ARG VARIANT="6.0" FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT} # Install Node.js @@ -28,4 +28,4 @@ RUN su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && npm install -g RUN su vscode -c "umask 0002 && cd /tmp && mkdir rust && cd rust && curl -fsSLo install_rust.sh https://sh.rustup.rs && chmod +x ./install_rust.sh && ./install_rust.sh -y -t wasm32-wasi" RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && apt-get -y install --no-install-recommends gcc gcc-multilib -WORKDIR / \ No newline at end of file +WORKDIR / diff --git a/.github/workflows/build-devcontainer.yml b/.github/workflows/build-devcontainer.yml index 01507ff57..57a4cc47f 100644 --- a/.github/workflows/build-devcontainer.yml +++ b/.github/workflows/build-devcontainer.yml @@ -41,11 +41,11 @@ jobs: file: .devcontainer/Dockerfile context: .devcontainer build-args: | - VARIANT : 5.0 + VARIANT : 6.0 NODE_VERSION: lts/* HIPPO_VERSION: v0.9.0 BINDLE_VERSION: v0.6.0 WAGI_VERSION: v0.4.0 push: true tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3a95262df..7592669fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/.github/workflows/manual-build-container.yml b/.github/workflows/manual-build-container.yml index 50702d191..d9e440833 100644 --- a/.github/workflows/manual-build-container.yml +++ b/.github/workflows/manual-build-container.yml @@ -18,7 +18,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x - name: Set MINVERBUILDMETADATA and Image Version Tag run: | @@ -40,8 +40,8 @@ jobs: - name: Copy openssl conf shell: bash run: | - mkdir -p ./Hippo/bin/Release/net5.0/linux-x64/publish/certs - cp .github/release-image/localhost.conf ./Hippo/bin/Release/net5.0/linux-x64/publish/certs + mkdir -p ./Hippo/bin/Release/net6.0/linux-x64/publish/certs + cp .github/release-image/localhost.conf ./Hippo/bin/Release/net6.0/linux-x64/publish/certs - name: Log in to the Container registry uses: docker/login-action@v1 @@ -67,7 +67,7 @@ jobs: uses: docker/build-push-action@v2 with: file: .github/release-image/Dockerfile - context: ./Hippo/bin/Release/net5.0/linux-x64/publish + context: ./Hippo/bin/Release/net6.0/linux-x64/publish push: true tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 92d4202c7..d7673a805 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x - name: Set MINVERBUILDMETADATA run: echo MINVERBUILDMETADATA=$(git rev-parse --short ${GITHUB_SHA}) >> $GITHUB_ENV @@ -60,7 +60,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x - name: Set MINVERBUILDMETADATA run: echo MINVERBUILDMETADATA=$(git rev-parse --short ${GITHUB_SHA}) >> $GITHUB_ENV @@ -107,7 +107,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x - name: Set MINVERBUILDMETADATA run: echo MINVERBUILDMETADATA=$(git rev-parse --short ${GITHUB_SHA}) >> $GITHUB_ENV @@ -126,8 +126,8 @@ jobs: - name: Copy openssl conf shell: bash run: | - mkdir -p ./Hippo/bin/Release/net5.0/linux-x64/publish/certs - cp .github/release-image/localhost.conf ./Hippo/bin/Release/net5.0/linux-x64/publish/certs + mkdir -p ./Hippo/bin/Release/net6.0/linux-x64/publish/certs + cp .github/release-image/localhost.conf ./Hippo/bin/Release/net6.0/linux-x64/publish/certs - name: Log in to the Container registry uses: docker/login-action@v1 @@ -153,7 +153,7 @@ jobs: uses: docker/build-push-action@v2 with: file: .github/release-image/Dockerfile - context: ./Hippo/bin/Release/net5.0/linux-x64/publish + context: ./Hippo/bin/Release/net6.0/linux-x64/publish push: true tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file + labels: ${{ steps.meta.outputs.labels }} diff --git a/.vscode/launch.json b/.vscode/launch.json index 05757d1f1..ae4e677c3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/Hippo/bin/Debug/net5.0/hippo-server.dll", + "program": "${workspaceFolder}/Hippo/bin/Debug/net6.0/hippo-server.dll", "args": [], "cwd": "${workspaceFolder}/Hippo", "stopAtEntry": false, @@ -33,4 +33,4 @@ "processId": "${command:pickProcess}" } ] -} \ No newline at end of file +} diff --git a/Hippo.Tests/ApiControllers/ApplicationControllerTests.cs b/Hippo.Tests/ApiControllers/ApplicationControllerTests.cs deleted file mode 100644 index 422786fd2..000000000 --- a/Hippo.Tests/ApiControllers/ApplicationControllerTests.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Net.Http.Headers; -using System.Net.Http.Json; -using System.Security.Claims; -using System.Security.Principal; -using System.Threading; -using System.Threading.Tasks; -using Hippo.ApiControllers; -using Hippo.Messages; -using Hippo.Models; -using Hippo.Repositories; -using Hippo.Tasks; -using Hippo.Tests.Fakes; -using Hippo.Tests.Stubs; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.TestHost; -using Microsoft.EntityFrameworkCore; -using Moq; -using Xunit; - -namespace Hippo.Tests.ApiControllers -{ - public class ApplicationControllerTestFixture : BaseApiTestFixture - { - const string TestDatabaseName = "HippoApplicationApiControllerTest"; - public CreateApplicationRequest CreateApplicationRequest = new() - { - ApplicationName = "Test Application", - StorageId = "hippo/test" - }; - public readonly UserManager UserManager; - - public ApplicationControllerTestFixture() : base(TestDatabaseName) - { - var store = new Mock>(); - store - .Setup(x => x.FindByIdAsync("2", CancellationToken.None)) - .ReturnsAsync(User); - Context = new InMemoryDataContext(TestDatabaseName); - UserManager = new UserManager(store.Object, null, null, null, null, null, null, null, null); - Server = new TestServer( - new WebHostBuilder(). - UseStartup( - context => new TestStartup(TokenIssuer, TestDatabaseName, new FakeTaskQueue()) - ) - ); - } - } - - public class ApplicationControllerTest : IClassFixture - { - private readonly ApplicationControllerTestFixture _fixture; - public ApplicationControllerTest(ApplicationControllerTestFixture fixture) - { - this._fixture = fixture; - } - - [Fact] - public async Task RequiresAuthorization() - { - var client = _fixture.Server.CreateClient(); - var response = await client.PostAsJsonAsync("/api/application", _fixture.CreateApplicationRequest); - Assert.True(response.StatusCode == System.Net.HttpStatusCode.Unauthorized); - } - - [Fact] - public async Task PostApplicationSucceeds() - { - var token = _fixture.TokenIssuer.GetToken(); - var client = _fixture.Server.CreateClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); - var response = await client.PostAsJsonAsync("/api/application", _fixture.CreateApplicationRequest); - Assert.True(response.IsSuccessStatusCode); - var createApplicationResponse = await response.Content.ReadFromJsonAsync(); - Assert.NotEqual(createApplicationResponse.Id, Guid.Empty); - Assert.Equal(_fixture.CreateApplicationRequest.ApplicationName, createApplicationResponse.ApplicationName); - Assert.Equal(_fixture.CreateApplicationRequest.StorageId, createApplicationResponse.StorageId); - } - - [Fact] - public async Task CreateApplicationSucceeds() - { - var controller = new ApplicationController(new DbUnitOfWork(_fixture.Context, new FakeCurrentUser(_fixture.User.UserName)), _fixture.UserManager, new FakeTaskQueue(), new NullLogger()) - { - ControllerContext = new() - { - HttpContext = new DefaultHttpContext - { - User = new ClaimsPrincipal(new GenericIdentity(_fixture.User.UserName)) - } - } - }; - var response = await controller.New(_fixture.CreateApplicationRequest); - Assert.NotNull(response); - Assert.IsType(response.Result); - var createdResult = response.Result as CreatedResult; - Assert.IsType(createdResult.Value); - var result = createdResult.Value as CreateApplicationResponse; - Assert.NotEqual(result.Id, Guid.Empty); - Assert.Equal(_fixture.CreateApplicationRequest.ApplicationName, result.ApplicationName); - Assert.Equal(_fixture.CreateApplicationRequest.StorageId, result.StorageId); - } - - [Fact] - public async Task InvalidModelCausesBadRequesstError() - { - var controller = GetController(); - controller.ModelState.AddModelError("Test", "TestError"); - var response = await controller.New(_fixture.CreateApplicationRequest); - Assert.NotNull(response); - Assert.IsType(response.Result); - } - - [Fact] - public void InvalidModelCausesError() - { - var requests = new CreateApplicationRequest[] - { - new() - { - ApplicationName = "Storage Id is missing" - }, - new() - { - StorageId = "hippo/test" - } - }; - - foreach (var request in requests) - { - var context = new ValidationContext(request); - var validationResults = new List(); - var result = Validator.TryValidateObject(request, context, validationResults, true); - Assert.False(result); - } - } - - private ApplicationController GetController() - => new(new DbUnitOfWork(_fixture.Context, new FakeCurrentUser(_fixture.User.UserName)), _fixture.UserManager, new FakeTaskQueue(), new NullLogger()) - { - ControllerContext = new() - { - HttpContext = new DefaultHttpContext - { - User = new ClaimsPrincipal(new GenericIdentity(_fixture.User.UserName)) - } - } - }; - } -} diff --git a/Hippo.Tests/ApiControllers/BaseApiTestFixture.cs b/Hippo.Tests/ApiControllers/BaseApiTestFixture.cs deleted file mode 100644 index 4c50f0e98..000000000 --- a/Hippo.Tests/ApiControllers/BaseApiTestFixture.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Net.Http.Json; -using System.Security.Claims; -using System.Security.Principal; -using System.Threading; -using System.Threading.Tasks; -using Hippo.ApiControllers; -using Hippo.Messages; -using Hippo.Models; -using Hippo.Repositories; -using Hippo.Tasks; -using Hippo.Tests.Stubs; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.TestHost; -using Microsoft.EntityFrameworkCore; -using Moq; -using Xunit; - -namespace Hippo.Tests.ApiControllers -{ - public class BaseApiTestFixture - { - public MockTokenIssuer TokenIssuer { get; private set; } - public TestServer Server { get; protected set; } - public DataContext Context { get; protected set; } - public Guid AppId { get; private set; } - public Application Application { get; private set; } - public Account User { get; private set; } - - public BaseApiTestFixture(string testDatabaseName) - { - AppId = Guid.NewGuid(); - User = new() - { - UserName = "user", - Id = "2", - }; - Application = new() - { - Id = AppId, - Name = "Test Application", - StorageId = "hippo/test", - Owner = User, - Revisions = new List { - new Revision { RevisionNumber = "1.2.3" }, - }, - }; - Context = new InMemoryDataContext(testDatabaseName); - Context.Database.EnsureDeleted(); - Context.Database.EnsureCreated(); - Context.Applications.Add(Application); - Context.Users.Add(User); - Context.SaveChanges(); - TokenIssuer = new MockTokenIssuer(); - } - } -} diff --git a/Hippo.Tests/ApiControllers/ChannelControllerTests.cs b/Hippo.Tests/ApiControllers/ChannelControllerTests.cs deleted file mode 100644 index 06695fb14..000000000 --- a/Hippo.Tests/ApiControllers/ChannelControllerTests.cs +++ /dev/null @@ -1,248 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Net.Http.Json; -using System.Security.Claims; -using System.Security.Principal; -using System.Threading; -using System.Threading.Tasks; -using Hippo.ApiControllers; -using Hippo.Messages; -using Hippo.Models; -using Hippo.Repositories; -using Hippo.Tasks; -using Hippo.Tests.Stubs; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.TestHost; -using Moq; -using Xunit; - -namespace Hippo.Tests.ApiControllers -{ - public class ChannelControllerTestFixture : BaseApiTestFixture - { - const string TestDatabaseName = "Hippo"; - public CreateChannelRequest CreateChannelRequestFixed { get; private set; } - public CreateChannelRequest CreateChannelRequestRange { get; private set; } - public CreateChannelRequest CreateChannelRequestInvalidApp { get; private set; } - public readonly Mock> MockTaskQueue; - - public ChannelControllerTestFixture() : base(TestDatabaseName) - { - CreateChannelRequestFixed = new() - { - AppId = AppId, - Name = "Test Channel with Fixed Revision", - Domain = "test.hippofactory.io", - RevisionSelectionStrategy = ChannelRevisionSelectionStrategy.UseSpecifiedRevision, - RevisionNumber = "1.2.3" - }; - - CreateChannelRequestInvalidApp = new() - { - AppId = Guid.NewGuid(), - Name = "Test Channel with Invalid App", - Domain = "test.hippofactory.io", - RevisionSelectionStrategy = ChannelRevisionSelectionStrategy.UseSpecifiedRevision, - RevisionNumber = "1.2.3" - }; - - CreateChannelRequestRange = new() - { - AppId = AppId, - Name = "Test Channel with Range Revision", - Domain = "test.hippofactory.io", - RevisionSelectionStrategy = ChannelRevisionSelectionStrategy.UseRangeRule, - RevisionRange = "~1.2.3" - }; - MockTaskQueue = new Mock>(); - MockTaskQueue.Setup(tq => tq.Enqueue(It.IsAny(), It.IsAny())).Verifiable(); - Server = new TestServer( - new WebHostBuilder(). - UseStartup( - context => new TestStartup(TokenIssuer, TestDatabaseName, MockTaskQueue.Object) - ) - ); - } - } - - public class ChannelControllerTest : IClassFixture - { - private readonly ChannelControllerTestFixture _fixture; - - public ChannelControllerTest(ChannelControllerTestFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task RequiresAuthorization() - { - var client = _fixture.Server.CreateClient(); - var response = await client.PostAsJsonAsync("/api/channel", _fixture.CreateChannelRequestFixed); - Assert.True(response.StatusCode == System.Net.HttpStatusCode.Unauthorized); - } - - [Fact] - public async Task PostChannelSucceeds() - { - var token = _fixture.TokenIssuer.GetToken(); - var client = _fixture.Server.CreateClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); - - // Create a channel with a fixed revsion - var response = await client.PostAsJsonAsync("/api/channel", _fixture.CreateChannelRequestFixed); - await CheckResultAsync(_fixture.CreateChannelRequestFixed, response); - - // Create a channel with a revsion range - response = await client.PostAsJsonAsync("/api/channel", _fixture.CreateChannelRequestRange); - await CheckResultAsync(_fixture.CreateChannelRequestRange, response, true); - } - - [Fact] - public async Task CreateChannelSucceeds() - { - var controller = GetController(); - - // Create a channel with a fixed revsion - var response = await controller.New(_fixture.CreateChannelRequestFixed); - CheckResult(_fixture.CreateChannelRequestFixed, response); - - // Create a channel with a revsion range - response = await controller.New(_fixture.CreateChannelRequestRange); - CheckResult(_fixture.CreateChannelRequestRange, response, true); - } - - [Fact] - public async Task InvalidApplicationCausesNotFoundError() - { - var controller = GetController(); - var response = await controller.New(_fixture.CreateChannelRequestInvalidApp); - Assert.NotNull(response); - Assert.IsType(response.Result); - } - - [Fact] - public async Task InvalidModelCausesBadRequesstError() - { - var controller = GetController(); - controller.ModelState.AddModelError("Test", "TestError"); - var response = await controller.New(_fixture.CreateChannelRequestInvalidApp); - Assert.NotNull(response); - Assert.IsType(response.Result); - } - - [Fact] - public void InvalidModelCausesError() - { - var requests = new CreateChannelRequest[] - { - new() - { - Name = "AppID is Missing", - Domain = "appidmissing.hippofactory.io", - RevisionSelectionStrategy = ChannelRevisionSelectionStrategy.UseSpecifiedRevision, - RevisionNumber = "1.2.3" - }, - new() - { - AppId = Guid.NewGuid(), - Domain = "namemissing.hippofactory.io", - RevisionSelectionStrategy = ChannelRevisionSelectionStrategy.UseSpecifiedRevision, - RevisionNumber = "1.2.3" - }, - new() - { - AppId = Guid.NewGuid(), - Name = "Domain is Missing", - RevisionSelectionStrategy = ChannelRevisionSelectionStrategy.UseSpecifiedRevision, - RevisionNumber = "1.2.3" - }, - new() - { - AppId = Guid.NewGuid(), - Name = "Revision Strategy is Missing", - Domain = "revisionstrategymissing.hippofactory.io", - RevisionNumber = "1.2.3" - }, - new() - { - AppId = Guid.NewGuid(), - Name = "Revision Range is Missing", - Domain = "revisionrangemissing.hippofactory.io", - RevisionNumber = "1.2.3", - RevisionSelectionStrategy = ChannelRevisionSelectionStrategy.UseRangeRule, - }, - new() - { - AppId = Guid.NewGuid(), - Name = "Revision Version is Missing", - Domain = "revisionversionmissing.hippofactory.io", - RevisionRange = "~1.2.3", - RevisionSelectionStrategy = ChannelRevisionSelectionStrategy.UseSpecifiedRevision, - } - }; - - foreach (var request in requests) - { - var context = new ValidationContext(request); - var validationResults = new List(); - var result = Validator.TryValidateObject(request, context, validationResults, true); - Assert.False(result); - } - } - - private void CheckResult(CreateChannelRequest request, ActionResult response, bool range = false) - { - Assert.NotNull(response); - Assert.IsType(response.Result); - var createdResult = response.Result as CreatedResult; - Assert.IsType(createdResult.Value); - var result = createdResult.Value as CreateChannelResponse; - CheckResult(request, result, range); - } - - private async Task CheckResultAsync(CreateChannelRequest request, HttpResponseMessage response, bool range = false) - { - Assert.True(response.IsSuccessStatusCode); - var createChannelResponse = await response.Content.ReadFromJsonAsync(); - CheckResult(request, createChannelResponse, range); - } - - private void CheckResult(CreateChannelRequest request, CreateChannelResponse result, bool range = false) - { - Assert.NotEqual(Guid.Empty, result.Id); - Assert.Equal(request.Name, result.Name); - if (range) - { - Assert.Equal(ChannelRevisionSelectionStrategy.UseRangeRule, result.RevisionSelectionStrategy); - Assert.Equal(request.RevisionRange, result.RevisionRange); - Assert.True(string.IsNullOrEmpty(result.RevisionNumber)); - } - else - { - Assert.Equal(ChannelRevisionSelectionStrategy.UseSpecifiedRevision, result.RevisionSelectionStrategy); - Assert.Equal(request.RevisionNumber, result.RevisionNumber); - Assert.True(string.IsNullOrEmpty(result.RevisionRange)); - } - - _fixture.MockTaskQueue.Verify(tq => tq.Enqueue(It.Is(cr => cr.ApplicationId == request.AppId && cr.ChannelId == result.Id), It.IsAny()), Times.Once); - } - - private ChannelController GetController() - => new(new DbUnitOfWork(_fixture.Context, new FakeCurrentUser(_fixture.User.UserName)), null, _fixture.MockTaskQueue.Object, new NullLogger()) - { - ControllerContext = new() - { - HttpContext = new DefaultHttpContext - { - User = new ClaimsPrincipal(new GenericIdentity(_fixture.User.UserName)) - } - } - }; - } -} diff --git a/Hippo.Tests/ApiControllers/MockTokenIssuer.cs b/Hippo.Tests/ApiControllers/MockTokenIssuer.cs deleted file mode 100644 index c0d1e00b6..000000000 --- a/Hippo.Tests/ApiControllers/MockTokenIssuer.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; -using System.Security.Cryptography; -using Microsoft.IdentityModel.Tokens; - -namespace Hippo.Tests.ApiControllers -{ - public class MockTokenIssuer - { - public readonly string Issuer; - public readonly string Audience; - public readonly SecurityKey SecurityKey; - private readonly Claim[] _claims; - - public MockTokenIssuer() - { - Issuer = Guid.NewGuid().ToString(); - Audience = Guid.NewGuid().ToString(); - var key = new byte[32]; - RandomNumberGenerator.Create().GetBytes(key); - SecurityKey = new SymmetricSecurityKey(key) - { - KeyId = Guid.NewGuid().ToString() - }; - _claims = new[] - { - new Claim(JwtRegisteredClaimNames.Sub, "user@test.com"), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), - new Claim(JwtRegisteredClaimNames.UniqueName, "user") - }; - } - - public string GetToken(IEnumerable claims) - { - var tokenHandler = new JwtSecurityTokenHandler(); - return tokenHandler.WriteToken( - new JwtSecurityToken( - issuer: Issuer, - audience: Audience, - claims: claims, - expires: DateTime.UtcNow.AddMinutes(30), - signingCredentials: - new SigningCredentials( - key: SecurityKey, - algorithm: SecurityAlgorithms.HmacSha256))); - } - - public string GetToken() - { - return GetToken(_claims); - } - } -} diff --git a/Hippo.Tests/ApiControllers/TestStartup.cs b/Hippo.Tests/ApiControllers/TestStartup.cs deleted file mode 100644 index 7616f0721..000000000 --- a/Hippo.Tests/ApiControllers/TestStartup.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Reflection; -using Hippo.Models; -using Hippo.Repositories; -using Hippo.Tasks; -using Hippo.Tests.Stubs; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.IdentityModel.Tokens; -using Xunit; - -namespace Hippo.Tests.ApiControllers -{ - public class TestStartup - { - private readonly MockTokenIssuer _tokenIssuer; - private readonly ITaskQueue _taskQueue; - private readonly string _testDatabaseName; - - public TestStartup(MockTokenIssuer tokenIssuer, string testDatabaseName, ITaskQueue taskQueue) - { - Assert.NotNull(tokenIssuer); - _tokenIssuer = tokenIssuer; - Assert.False(string.IsNullOrEmpty(testDatabaseName)); - _testDatabaseName = testDatabaseName; - _taskQueue = taskQueue; - } - - public void ConfigureServices(IServiceCollection services) - { - services.AddIdentity(cfg => - { - cfg.User.RequireUniqueEmail = true; - }).AddEntityFrameworkStores(); - services.AddControllers().AddApplicationPart(Assembly.Load("hippo-server")).AddControllersAsServices(); - - services.AddAuthentication().AddJwtBearer( - cfg => cfg.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuer = true, - ValidateAudience = true, - ValidateLifetime = true, - ValidateIssuerSigningKey = true, - ValidIssuer = _tokenIssuer.Issuer, - ValidAudience = _tokenIssuer.Audience, - IssuerSigningKey = _tokenIssuer.SecurityKey - } - ); - - services.AddAuthorization(options => - { - options.AddPolicy("RequireAdministratorRole", - policy => policy.RequireRole("Administrator")); - }); - - services.AddSingleton(); - services.AddDbContext( - options => - options.UseInMemoryDatabase(_testDatabaseName) - ); - - services.AddScoped(); - services.AddScoped(); - if (_taskQueue != null) - { - services.AddSingleton>(_taskQueue); - } - - services.AddRouting(options => options.LowercaseUrls = true); - services.AddMvc(); - } - - public void Configure(IApplicationBuilder app) - { - app.UseRouting(); - app.UseAuthentication(); - app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - }); - } - } -} diff --git a/Hippo.Tests/Controllers/AccountControllerTest.cs b/Hippo.Tests/Controllers/AccountControllerTest.cs deleted file mode 100644 index 97d507aee..000000000 --- a/Hippo.Tests/Controllers/AccountControllerTest.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Linq; -using System.Security.Claims; -using System.Security.Principal; -using System.Threading; -using Hippo.Controllers; -using Hippo.Models; -using Hippo.Repositories; -using Hippo.Tests.Stubs; -using Hippo.ViewModels; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Moq; -using Xunit; - -[assembly: System.CLSCompliant(false)] - -namespace Hippo.Tests.Controllers -{ - public class AccountControllerTest - { - private readonly AccountController _controller; - private readonly DataContext _context; - private readonly Account _admin; - - private readonly Account _user; - - public AccountControllerTest() - { - this._admin = new Account - { - UserName = "admin", - Id = "1", - }; - this._user = new Account - { - UserName = "user", - Id = "2" - }; - _context = new InMemoryDataContext(); - var configuration = new Mock(); - _controller = new AccountController(new FakeSignInManager(new FakeUserManager(_context)), new DbUnitOfWork(_context, new FakeCurrentUser(_admin.UserName)), configuration.Object, new NullLogger()); - var identity = new GenericIdentity(_admin.UserName); - var context = new ControllerContext - { - HttpContext = new DefaultHttpContext - { - User = new ClaimsPrincipal(identity) - } - }; - _controller.ControllerContext = context; - } - - [Fact] - public async void TestRegister() - { - var viewResult = _controller.Register(); - Assert.NotNull(viewResult); - - var registerResult = await _controller.Register(new AccountRegisterForm - { - UserName = "test", - Email = "test@hippofactory.io", - Password = "foobar", - PasswordConfirm = "foobar", - }); - Assert.NotNull(registerResult); - Assert.Equal(typeof(RedirectToActionResult), registerResult.GetType()); - Assert.Equal("Login", ((RedirectToActionResult)registerResult).ActionName); - - Assert.NotNull(_context.Accounts.Where(a => a.UserName == "test").SingleOrDefault()); - } - } -} diff --git a/Hippo.Tests/Controllers/AppControllerTest.cs b/Hippo.Tests/Controllers/AppControllerTest.cs deleted file mode 100644 index 1666e8e81..000000000 --- a/Hippo.Tests/Controllers/AppControllerTest.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.Linq; -using System.Security.Claims; -using System.Security.Principal; -using System.Threading; -using System.Threading.Tasks; -using Hippo.Controllers; -using Hippo.Models; -using Hippo.Repositories; -using Hippo.Tasks; -using Hippo.Tests.Fakes; -using Hippo.Tests.Schedulers; -using Hippo.Tests.Stubs; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Moq; -using Xunit; - -namespace Hippo.Tests.Controllers -{ - public class AppControllerTest - { - private readonly Account admin; - - private readonly Account user; - - public AppControllerTest() - { - admin = new Account - { - UserName = "admin", - Id = "1", - }; - user = new Account - { - UserName = "user", - Id = "2" - }; - } - - private AppController MakeController(string dbName) - { - var store = new Mock>(); - store.Setup(x => x.FindByIdAsync("1", CancellationToken.None)) - .ReturnsAsync(admin); - store.Setup(x => x.FindByIdAsync("2", CancellationToken.None)) - .ReturnsAsync(user); - store.Setup(x => x.FindByNameAsync("admin", CancellationToken.None)) - .ReturnsAsync(admin); - store.Setup(x => x.FindByNameAsync("user", CancellationToken.None)) - .ReturnsAsync(user); - var context = DbContext(dbName); - var userManager = new UserManager(store.Object, null, null, null, null, null, null, null, null); - var taskQueue = new FakeTaskQueue(); - var unitOfWork = new DbUnitOfWork(context, new FakeCurrentUser(admin.UserName)); - var controller = new AppController(unitOfWork, userManager, taskQueue, new NullLogger()); - return controller; - } - - private static DataContext DbContext(string dbName) - { - return new InMemoryDataContext(dbName); - } - - [Fact] - public void TestGetApps() - { - var fakeIdentity = new GenericIdentity(admin.UserName); - var context = new ControllerContext - { - HttpContext = new DefaultHttpContext - { - User = new ClaimsPrincipal(fakeIdentity) - } - }; - var controller = MakeController("testgetapps"); - controller.ControllerContext = context; - - var viewResult = controller.Index(); - Assert.NotNull(viewResult); - } - - [Fact] - public async Task EditAppStoresAllFields() - { - var fakeIdentity = new GenericIdentity(admin.UserName); - var context = new ControllerContext - { - HttpContext = new DefaultHttpContext - { - User = new ClaimsPrincipal(fakeIdentity) - } - }; - - // Simulate a request to New - - var controllerN = MakeController("editapps"); - controllerN.ControllerContext = context; - - Assert.Equal(0, DbContext("editapps").Applications.Count()); - await controllerN.New(new ViewModels.AppNewForm { Name = "foo", StorageId = "contoso/birdsondemand" }); - Assert.Equal(1, DbContext("editapps").Applications.Count()); - - var appN = DbContext("editapps").Applications.Single(); - Assert.Equal("foo", appN.Name); - Assert.Equal("contoso/birdsondemand", appN.StorageId); - - // Simulate a request to Edit - - var contextE = new ControllerContext - { - HttpContext = new DefaultHttpContext - { - User = new ClaimsPrincipal(fakeIdentity) - } - }; - - var controllerE = MakeController("editapps"); - controllerE.ControllerContext = contextE; - - var res = await controllerE.Edit(appN.Id, new ViewModels.AppEditForm { Id = appN.Id, Name = "bar", StorageId = "contoso/birdsdoingtheirownthing" }); - - var appE = DbContext("editapps").Applications.Single(); - Assert.Equal("bar", appE.Name); - Assert.Equal("contoso/birdsdoingtheirownthing", appE.StorageId); - } - } -} diff --git a/Hippo.Tests/Fakes/FaskTaskQueue.cs b/Hippo.Tests/Fakes/FaskTaskQueue.cs deleted file mode 100644 index 3c45341f3..000000000 --- a/Hippo.Tests/Fakes/FaskTaskQueue.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Hippo.Tasks; - -namespace Hippo.Tests.Fakes -{ - public class FakeTaskQueue : ITaskQueue - { - private readonly Queue _impl = new(); - - public Task Dequeue(CancellationToken cancellationToken) - { - return Task.FromResult(_impl.Dequeue()); - } - - public Task Enqueue(T value, CancellationToken cancellationToken) - { - _impl.Enqueue(value); - return Task.CompletedTask; - } - - public (bool, T) TryRead() - { - var item = _impl.Dequeue(); - return (item is not null, item); - } - } -} diff --git a/Hippo.Tests/Schedulers/FakeJobScheduler.cs b/Hippo.Tests/Schedulers/FakeJobScheduler.cs deleted file mode 100644 index add36602a..000000000 --- a/Hippo.Tests/Schedulers/FakeJobScheduler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using Hippo.Models; -using Hippo.Schedulers; - -namespace Hippo.Tests.Schedulers -{ - public class FakeJobScheduler : IJobScheduler - { - public void OnSchedulerStart(IEnumerable applications) - { - // no-op - } - - public void Start(Channel c) - { - // no-op - } - - public void Stop(Channel c) - { - // no-op - } - } -} diff --git a/Hippo.Tests/Stubs/FakeCurrentUser.cs b/Hippo.Tests/Stubs/FakeCurrentUser.cs deleted file mode 100644 index ee0e17df4..000000000 --- a/Hippo.Tests/Stubs/FakeCurrentUser.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Security.Claims; -using System.Security.Principal; -using System.Threading; -using Hippo.Controllers; -using Hippo.Models; -using Hippo.Repositories; -using Hippo.Tests.Schedulers; -using Hippo.Tests.Stubs; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Moq; -using Xunit; - -namespace Hippo.Tests.Stubs -{ - class FakeCurrentUser : ICurrentUser - { - private readonly string _name; - - public FakeCurrentUser(string name) - { - _name = name; - } - - public string Name() => _name; - } -} diff --git a/Hippo.Tests/Stubs/FakeSignInManager.cs b/Hippo.Tests/Stubs/FakeSignInManager.cs deleted file mode 100644 index 8083d1d66..000000000 --- a/Hippo.Tests/Stubs/FakeSignInManager.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Threading.Tasks; -using Hippo.Models; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Moq; - -namespace Hippo.Tests.Stubs -{ - public class FakeSignInManager : SignInManager - { - public FakeSignInManager() - : base(new FakeUserManager(), - new Mock().Object, - new Mock>().Object, - new Mock>().Object, - new Mock>>().Object, - new Mock().Object, - new Mock>().Object) - { } - - public FakeSignInManager(FakeUserManager userManager) - : base(userManager, - new Mock().Object, - new Mock>().Object, - new Mock>().Object, - new Mock>>().Object, - new Mock().Object, - new Mock>().Object) - { } - } - - public class FakeUserManager : UserManager - { - private readonly DataContext _context; - - public FakeUserManager() - : base(new Mock>().Object, - new Mock>().Object, - new Mock>().Object, - System.Array.Empty>(), - System.Array.Empty>(), - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock>>().Object) - { - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "Hippo") - .Options; - _context = new InMemoryDataContext(); - } - - public FakeUserManager(DataContext context) - : base(new Mock>().Object, - new Mock>().Object, - new Mock>().Object, - System.Array.Empty>(), - System.Array.Empty>(), - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock>>().Object) - { - _context = context; - } - - public override Task CreateAsync(Account user, string password) - { - _context.Accounts.Add(user); - _context.SaveChanges(); - return Task.FromResult(IdentityResult.Success); - } - - public override Task AddToRoleAsync(Account user, string role) - { - return Task.FromResult(IdentityResult.Success); - } - - public override Task GenerateEmailConfirmationTokenAsync(Account user) - { - return Task.FromResult(Guid.NewGuid().ToString()); - } - - } - -} diff --git a/Hippo.Tests/Stubs/NullLogger.cs b/Hippo.Tests/Stubs/NullLogger.cs deleted file mode 100644 index ddb7d5f16..000000000 --- a/Hippo.Tests/Stubs/NullLogger.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; - -namespace Hippo.Tests.Stubs -{ - internal class NullLogger : ILogger - { - private readonly bool _log; - - public NullLogger(bool log = false) - { - _log = log; - } - public IDisposable BeginScope(TState state) => - throw new NotImplementedException(); - - public bool IsEnabled(LogLevel logLevel) => true; - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - if (_log) - { - Console.WriteLine(state); - Console.WriteLine(exception); - } - } - } -} diff --git a/Hippo.Tests/.editorconfig b/Hippo.UnitTests/.editorconfig similarity index 100% rename from Hippo.Tests/.editorconfig rename to Hippo.UnitTests/.editorconfig diff --git a/Hippo.Tests/Hippo.Tests.csproj b/Hippo.UnitTests/Hippo.UnitTests.csproj similarity index 79% rename from Hippo.Tests/Hippo.Tests.csproj rename to Hippo.UnitTests/Hippo.UnitTests.csproj index bb16aebf6..c913308c0 100644 --- a/Hippo.Tests/Hippo.Tests.csproj +++ b/Hippo.UnitTests/Hippo.UnitTests.csproj @@ -1,28 +1,26 @@ - - - - net5.0 - - false - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - + + + + net6.0 + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Hippo.Tests/Models/ApplicationTest.cs b/Hippo.UnitTests/Models/ApplicationTest.cs similarity index 62% rename from Hippo.Tests/Models/ApplicationTest.cs rename to Hippo.UnitTests/Models/ApplicationTest.cs index cd027a4dc..1eeec5587 100644 --- a/Hippo.Tests/Models/ApplicationTest.cs +++ b/Hippo.UnitTests/Models/ApplicationTest.cs @@ -61,49 +61,5 @@ public void ReevaluationUpdatesAllChannels() Assert.Equal("0.6.1", app.Channels.ElementAt(0).ActiveRevision.RevisionNumber); Assert.Equal("1.1.6", app.Channels.ElementAt(1).ActiveRevision.RevisionNumber); } - - public ApplicationTest() - { - // One = new Application - // { - // Name = "one", - // Owner = new Account - // { - // UserName = "user", - // }, - // Collaborators = new List(), - // Releases = new List - // { - // new Release - // { - // Revision = "2.0.0", - // UploadUrl = "bindle:hippofactory.io/one/2.0.0" - // } - // }, - // Channels = new List - // { - // new Channel - // { - // Name = "development", - // Release = new Release - // { - // Revision = "1.0.0", - // UploadUrl = "bindle:hippofactory.io/one/1.0.0" - // }, - // Configuration = new Configuration - // { - // EnvironmentVariables = new List - // { - // new EnvironmentVariable - // { - // Key = "HELLO", - // Value = "world" - // } - // } - // }, - // } - // } - // }; - } } } diff --git a/Hippo.Tests/Models/ChannelTest.cs b/Hippo.UnitTests/Models/ChannelTest.cs similarity index 100% rename from Hippo.Tests/Models/ChannelTest.cs rename to Hippo.UnitTests/Models/ChannelTest.cs diff --git a/Hippo.Tests/Rules/RevisionRangeRuleTests.cs b/Hippo.UnitTests/Rules/RevisionRangeRuleTests.cs similarity index 92% rename from Hippo.Tests/Rules/RevisionRangeRuleTests.cs rename to Hippo.UnitTests/Rules/RevisionRangeRuleTests.cs index d0a465c8b..5a124643f 100644 --- a/Hippo.Tests/Rules/RevisionRangeRuleTests.cs +++ b/Hippo.UnitTests/Rules/RevisionRangeRuleTests.cs @@ -27,7 +27,6 @@ public class RevisionRangeRuleTests // feature for 1.4.0, we should be able to set up a channel for the 1.4.0-spongiforms // latest prerelease. (Alice and Bob may still want individual channels though.) // - This is also relevant for "canary deployment of a feature branch." - private static readonly List _revisions = new string[] { "1.1.0", "1.1.1", @@ -79,14 +78,6 @@ public void SupportsMostRecentReleaseByUserWithinAVersion() AssertMatchResult("1.1.4-alice-2021.01.02.03.04.05.678", RevisionRangeRule.Parse("P:1.1.4-alice-*")); } - // [Fact] - // public void SupportsMostRecentReleaseByUserWithinVersionRange() - // { - // // TODO: may not need this - // // TODO: define behaviour - should higher version or more recent timestamp take precedence? - // AssertMatchResult("1.1.4-alice-2021.01.02.03.04.05.678", RevisionRangeRule.Parse("P:~1.1-alice-*")); - // } - [Fact] public void SupportsLongRunningFeatureBranches() { diff --git a/Hippo.sln b/Hippo.sln index 8cb7858ff..ce1cdd3f6 100644 --- a/Hippo.sln +++ b/Hippo.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 16.0.31129.286 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hippo", "Hippo\Hippo.csproj", "{8927CEEA-0EF3-41F0-A5B9-B368777F8F09}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hippo.Test", "Hippo.Tests\Hippo.Tests.csproj", "{CDC0A540-3CB4-4130-B3A0-C808E61F92FE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hippo.UnitTests", "Hippo.UnitTests\Hippo.UnitTests.csproj", "{CDC0A540-3CB4-4130-B3A0-C808E61F92FE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Hippo/.vscode/launch.json b/Hippo/.vscode/launch.json index 9a386b40b..d1979ba8c 100644 --- a/Hippo/.vscode/launch.json +++ b/Hippo/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/bin/Debug/net5.0/Hippo.dll", + "program": "${workspaceFolder}/bin/Debug/net6.0/Hippo.dll", "args": [], "cwd": "${workspaceFolder}", "stopAtEntry": false, @@ -32,4 +32,4 @@ "request": "attach" } ] -} \ No newline at end of file +} diff --git a/Hippo/Hippo.csproj b/Hippo/Hippo.csproj index ddb757109..764e94dcd 100644 --- a/Hippo/Hippo.csproj +++ b/Hippo/Hippo.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 true 5 hippo-server