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
15 changes: 15 additions & 0 deletions Answer.King.sln
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Answer.King.Api.IntegrationTests", "tests\Answer.King.Api.IntegrationTests\Answer.King.Api.IntegrationTests.csproj", "{D660F11B-872D-4536-A8BC-FFC86118F469}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Answer.King.Api.ContractTests", "tests\Answer.King.Api.ContractTests\Answer.King.Api.ContractTests.csproj", "{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -123,6 +125,18 @@ Global
{D660F11B-872D-4536-A8BC-FFC86118F469}.Release|x64.Build.0 = Release|Any CPU
{D660F11B-872D-4536-A8BC-FFC86118F469}.Release|x86.ActiveCfg = Release|Any CPU
{D660F11B-872D-4536-A8BC-FFC86118F469}.Release|x86.Build.0 = Release|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Debug|x64.ActiveCfg = Debug|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Debug|x64.Build.0 = Debug|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Debug|x86.ActiveCfg = Debug|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Debug|x86.Build.0 = Debug|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Release|Any CPU.Build.0 = Release|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Release|x64.ActiveCfg = Release|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Release|x64.Build.0 = Release|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Release|x86.ActiveCfg = Release|Any CPU
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -135,6 +149,7 @@ Global
{625E8357-4943-46FB-AB0E-C25CF0A3AB02} = {F3A9A152-CBD9-4004-9745-1478B24AEB9B}
{4C8C0B44-7F02-4F69-A3BA-0AD7E9231BAD} = {F3A9A152-CBD9-4004-9745-1478B24AEB9B}
{D660F11B-872D-4536-A8BC-FFC86118F469} = {F3A9A152-CBD9-4004-9745-1478B24AEB9B}
{B5E3A3F4-7EE5-4830-A1D6-E911A9E3A55A} = {F3A9A152-CBD9-4004-9745-1478B24AEB9B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B0807BAF-430C-45E0-89FB-DB6ABAF9D5C4}
Expand Down
5 changes: 3 additions & 2 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"sdk": {
"version": "6.0.403",
"rollForward": "latestFeature"
"version": "6.0.0",
"rollForward": "latestFeature",
"allowPrerelease": true
}
}
134 changes: 71 additions & 63 deletions src/Answer.King.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,80 +14,88 @@
using FluentValidation.AspNetCore;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);
namespace Answer.King.Api;
// <- Note the namespaces here

// Add services to the container.
var corsAllowAnyPolicy = "AllowAnyOrigin";
builder.Services.AddCors(options =>
public partial class Program
{
options.AddPolicy(corsAllowAnyPolicy,
policy =>
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var corsAllowAnyPolicy = "AllowAnyOrigin";
builder.Services.AddCors(options =>
{
options.AddPolicy(corsAllowAnyPolicy,
policy =>
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});

builder.Services.AddRouting(options => options.LowercaseUrls = true);
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
options.JsonSerializerOptions.Converters.Add(new ProductIdJsonConverter());
options.JsonSerializerOptions.Converters.Add(new CategoryIdJsonConverter());
});
builder.Services.AddRouting(options => options.LowercaseUrls = true);
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
options.JsonSerializerOptions.Converters.Add(new ProductIdJsonConverter());
options.JsonSerializerOptions.Converters.Add(new CategoryIdJsonConverter());
});

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.UseCustomSchemaIdSelectorStrategy();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.UseCustomSchemaIdSelectorStrategy();

options.SwaggerDoc("v1", new OpenApiInfo { Title = "Answer King API", Version = "v1" });
options.DocumentFilter<TagDescriptionsDocumentFilter>();
options.SchemaFilter<ValidationProblemDetailsSchemaFilter>();
options.SchemaFilter<EnumSchemaFilter>();
options.SchemaFilter<ProductCategorySchemaFilter>();
options.SchemaFilter<RemoveSchemasFilter>();
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Answer King API", Version = "v1" });
options.DocumentFilter<TagDescriptionsDocumentFilter>();
options.SchemaFilter<ValidationProblemDetailsSchemaFilter>();
options.SchemaFilter<EnumSchemaFilter>();
options.SchemaFilter<ProductCategorySchemaFilter>();
options.SchemaFilter<RemoveSchemasFilter>();

// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);

options.IncludeXmlComments(xmlPath);
options.EnableAnnotations();
});
options.IncludeXmlComments(xmlPath);
options.EnableAnnotations();
});

builder.Services.AddValidatorsFromAssemblyContaining<Program>();
builder.Services.AddFluentValidationAutoValidation(
_ => ValidatorOptions.Global.PropertyNameResolver = CamelCasePropertyNameResolver.ResolvePropertyName);
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
builder.Services.AddFluentValidationAutoValidation(
_ => ValidatorOptions.Global.PropertyNameResolver = CamelCasePropertyNameResolver.ResolvePropertyName);

builder.Services.ConfigureLiteDb(options =>
{
options.RegisterEntityMappingsFromAssemblyContaining<IEntityMapping>();
options.RegisterDataSeedingFromAssemblyContaining<ISeedData>();
});
builder.Services.ConfigureLiteDb(options =>
{
options.RegisterEntityMappingsFromAssemblyContaining<IEntityMapping>();
options.RegisterDataSeedingFromAssemblyContaining<ISeedData>();
});

builder.Services.AddTransient<IOrderRepository, OrderRepository>();
builder.Services.AddTransient<IProductRepository, ProductRepository>();
builder.Services.AddTransient<ICategoryRepository, CategoryRepository>();
builder.Services.AddTransient<IPaymentRepository, PaymentRepository>();
builder.Services.AddTransient<IOrderService, OrderService>();
builder.Services.AddTransient<IPaymentService, PaymentService>();
builder.Services.AddTransient<IProductService, ProductService>();
builder.Services.AddTransient<ICategoryService, CategoryService>();
builder.Services.AddTransient<IOrderRepository, OrderRepository>();
builder.Services.AddTransient<IProductRepository, ProductRepository>();
builder.Services.AddTransient<ICategoryRepository, CategoryRepository>();
builder.Services.AddTransient<IPaymentRepository, PaymentRepository>();
builder.Services.AddTransient<IOrderService, OrderService>();
builder.Services.AddTransient<IPaymentService, PaymentService>();
builder.Services.AddTransient<IProductService, ProductService>();
builder.Services.AddTransient<ICategoryService, CategoryService>();

var app = builder.Build();
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "Answer King API V1"));
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "Answer King API V1"));
}

app.UseLiteDb();
app.UseHttpsRedirection();
app.UseCors(corsAllowAnyPolicy);
app.UseAuthorization();
app.MapControllers();
app.UseLiteDb();
app.UseHttpsRedirection();
app.UseCors(corsAllowAnyPolicy);
app.UseAuthorization();
app.MapControllers();

app.Run();
app.Run();
}
//Add services , etc - the whole content of Program.cs
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="PactNet" Version="4.3.0" />
<PackageReference Include="PactNet.Abstractions" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Answer.King.Api.IntegrationTests\Answer.King.Api.IntegrationTests.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="contracts\answer-king-api-answer-king-ui.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
52 changes: 52 additions & 0 deletions tests/Answer.King.Api.ContractTests/CategoriesTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Alba;
using Answer.King.Api.IntegrationTests.Common;
using Answer.King.Api.IntegrationTests.Common.Models;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.VisualStudio.TestPlatform.TestHost;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using PactNet;
using PactNet.Infrastructure.Outputters;
using PactNet.Models;
using PactNet.Verifier;
using PactNet.Verifier.Messaging;
using Xunit;
using Xunit.Abstractions;
using System.Net;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Hosting;
using IPAddress = System.Net.IPAddress;

namespace Answer.King.Api.ContractTests;

public class AnswerKingVerifierTest
{
private readonly PactVerifierConfig config;

public AnswerKingVerifierTest(ITestOutputHelper output)
{
this.config = new PactVerifierConfig
{
Outputters = new List<IOutput> { new XUnitOutput(output) },
LogLevel = PactLogLevel.Debug
};
}

[Fact]
public void VerifyContract()
{
string pactPath = Path.Combine("contracts", "answer-king-api-answer-king-ui.json");
IPactVerifier verifier = new PactVerifier(this.config);
verifier.ServiceProvider("AnswerKingCS-API", new Uri("http://localhost:5000"))
.WithFileSource(new FileInfo(pactPath))
.WithRequestTimeout(TimeSpan.FromSeconds(2))
.WithSslVerificationDisabled()
.Verify();
}
}
1 change: 1 addition & 0 deletions tests/Answer.King.Api.ContractTests/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
19 changes: 19 additions & 0 deletions tests/Answer.King.Api.ContractTests/XUnitOutput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using PactNet.Infrastructure.Outputters;
using Xunit.Abstractions;

namespace Answer.King.Api.ContractTests;

public class XUnitOutput : IOutput
{
private readonly ITestOutputHelper output;

public XUnitOutput(ITestOutputHelper output)
{
this.output = output;
}

public void WriteLine(string line)
{
this.output.WriteLine(line);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"consumer": {
"name": "answer-king-ui"
},
"provider": {
"name": "answer-king-api"
},
"interactions": [
{
"description": "",
"providerState": "",
"request": {
"method": "GET",
"path": "/api/categories",
"headers": {
"host": "localhost:5173",
"proxy-connection": "keep-alive",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Cypress/6.3.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36",
"content-type": "application/json",
"accept": "*/*",
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
"referer": "http://localhost:5173/menu",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-US"
},
"body": "",
"query": ""
},
"response": {
"status": 200,
"headers": {
"content-type": "application/json"
},
"body": [
{
"id": 1,
"name": "Seafood",
"description": "Food from the oceans",
"createdOn": "2023-01-05T16:53:22.859+00:00",
"lastUpdated": "2023-01-06T06:53:22.859+00:00",
"products": [
1
],
"retired": false
},
{
"id": 2,
"name": "Sundries",
"description": "Things that go with things.",
"createdOn": "2023-01-04T16:53:22.867+00:00",
"lastUpdated": "2023-01-05T10:53:22.867+00:00",
"products": [
2
],
"retired": false
}
]
}
}
],
"metadata": {
"pactSpecification": {
"version": "2.0.0"
},
"client": {
"name": "pact-cypress-adapter",
"version": "1.3.0"
}
}
}
Loading