Skip to content

Commit ea30dc1

Browse files
committed
Added test two sample tests for kebab casing convention
1 parent 79940a9 commit ea30dc1

15 files changed

+428
-68
lines changed

Directory.Build.props

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,35 @@
11
<Project>
2-
<PropertyGroup>
3-
<NetCoreAppVersion>net5.0</NetCoreAppVersion>
4-
<AspNetCoreVersion>5.0.*</AspNetCoreVersion>
5-
<EFCoreVersion>5.0.*</EFCoreVersion>
6-
<NpgsqlPostgreSQLVersion>5.0.*</NpgsqlPostgreSQLVersion>
7-
<SwashbuckleVersion>6.2.*</SwashbuckleVersion>
8-
<JsonApiDotNetCoreVersionPrefix>4.2.0</JsonApiDotNetCoreVersionPrefix>
9-
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodingGuidelines.ruleset</CodeAnalysisRuleSet>
10-
<WarningLevel>9999</WarningLevel>
11-
</PropertyGroup>
2+
<PropertyGroup>
3+
<NetCoreAppVersion>net5.0</NetCoreAppVersion>
4+
<AspNetCoreVersion>5.0.*</AspNetCoreVersion>
5+
<EFCoreVersion>5.0.*</EFCoreVersion>
6+
<NpgsqlPostgreSQLVersion>5.0.*</NpgsqlPostgreSQLVersion>
7+
<SwashbuckleVersion>6.2.*</SwashbuckleVersion>
8+
<JsonApiDotNetCoreVersionPrefix>4.2.0</JsonApiDotNetCoreVersionPrefix>
9+
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodingGuidelines.ruleset</CodeAnalysisRuleSet>
10+
<WarningLevel>9999</WarningLevel>
11+
</PropertyGroup>
1212

13-
<ItemGroup>
14-
<PackageReference Include="JetBrains.Annotations" Version="2021.1.0" PrivateAssets="All" />
15-
<PackageReference Include="CSharpGuidelinesAnalyzer" Version="3.7.0" PrivateAssets="All" />
16-
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CSharpGuidelinesAnalyzer.config" Visible="False" />
17-
</ItemGroup>
13+
<ItemGroup>
14+
<PackageReference Include="JetBrains.Annotations" Version="2021.1.0" PrivateAssets="All"/>
15+
<PackageReference Include="CSharpGuidelinesAnalyzer" Version="3.7.0" PrivateAssets="All"/>
16+
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CSharpGuidelinesAnalyzer.config" Visible="False"/>
17+
</ItemGroup>
1818

19-
<PropertyGroup Condition="'$(Configuration)'=='Release'">
20-
<NoWarn>$(NoWarn);1591</NoWarn>
21-
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
22-
<GenerateDocumentationFile>true</GenerateDocumentationFile>
23-
</PropertyGroup>
19+
<PropertyGroup Condition="'$(Configuration)'=='Release'">
20+
<NoWarn>$(NoWarn);1591</NoWarn>
21+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
22+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
23+
</PropertyGroup>
2424

25-
<!-- Test Project Dependencies -->
26-
<PropertyGroup>
27-
<BogusVersion>33.1.1</BogusVersion>
28-
<CoverletVersion>3.1.0</CoverletVersion>
29-
<FluentAssertionsVersion>6.1.0</FluentAssertionsVersion>
30-
<MoqVersion>4.16.1</MoqVersion>
31-
<XUnitVersion>2.4.*</XUnitVersion>
32-
<TestSdkVersion>16.11.0</TestSdkVersion>
33-
</PropertyGroup>
25+
<!-- Test Project Dependencies -->
26+
<PropertyGroup>
27+
<BogusVersion>33.1.1</BogusVersion>
28+
<CoverletVersion>3.1.0</CoverletVersion>
29+
<FluentAssertionsVersion>6.1.0</FluentAssertionsVersion>
30+
<MoqVersion>4.16.1</MoqVersion>
31+
<XUnitVersion>2.4.*</XUnitVersion>
32+
<TestSdkVersion>16.11.0</TestSdkVersion>
33+
<BlushingPenguinVersion>1.0.*</BlushingPenguinVersion>
34+
</PropertyGroup>
3435
</Project>

src/JsonApiDotNetCore/ArgumentGuard.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ internal static class ArgumentGuard
1212
[AssertionMethod]
1313
[ContractAnnotation("value: null => halt")]
1414
public static void NotNull<T>([CanBeNull] [NoEnumeration] T value, [NotNull] [InvokerParameterName] string name)
15-
where T : class
1615
{
1716
if (value is null)
1817
{

test/OpenApiTests/LegacyOpenApiIntegration/LegacyOpenApiIntegrationTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public LegacyOpenApiIntegrationTests()
1919
UseController<FlightAttendantsController>();
2020
}
2121

22+
// TODO: This test fails when all tests are openapi tests run in parallel; something isn't going right with the fixtures.
2223
[Fact]
2324
public async Task Retrieved_document_matches_expected_document()
2425
{
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Text.Json.Serialization;
2+
using JetBrains.Annotations;
3+
using JsonApiDotNetCore.Configuration;
4+
using Microsoft.EntityFrameworkCore;
5+
using OpenApiTests.LegacyOpenApiIntegration;
6+
7+
namespace OpenApiTests.NamingConvention.KebabCase
8+
{
9+
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
10+
public sealed class KebabCaseNamingConventionStartup<TDbContext> : OpenApiStartup<TDbContext>
11+
where TDbContext : DbContext
12+
{
13+
protected override void SetJsonApiOptions(JsonApiOptions options)
14+
{
15+
base.SetJsonApiOptions(options);
16+
17+
options.SerializerOptions.PropertyNamingPolicy = JsonKebabCaseNamingPolicy.Instance;
18+
options.SerializerOptions.DictionaryKeyPolicy = JsonKebabCaseNamingPolicy.Instance;
19+
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
20+
}
21+
}
22+
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
using System;
2+
using System.Net.Http;
3+
using System.Text.Json;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using FluentAssertions;
7+
using TestBuildingBlocks;
8+
using Xunit;
9+
10+
namespace OpenApiTests.NamingConvention.KebabCase
11+
{
12+
public sealed class KebabCaseTests
13+
: IClassFixture<IntegrationTestContext<KebabCaseNamingConventionStartup<NamingConventionDbContext>, NamingConventionDbContext>>
14+
{
15+
private static Lazy<Task<JsonDocument>> _lazyOpenApiDocument;
16+
private readonly IntegrationTestContext<KebabCaseNamingConventionStartup<NamingConventionDbContext>, NamingConventionDbContext> _testContext;
17+
18+
public KebabCaseTests(IntegrationTestContext<KebabCaseNamingConventionStartup<NamingConventionDbContext>, NamingConventionDbContext> testContext)
19+
{
20+
_testContext = testContext;
21+
22+
_lazyOpenApiDocument ??= new Lazy<Task<JsonDocument>>(async () =>
23+
{
24+
testContext.UseController<SupermarketsController>();
25+
const string requestUrl = "swagger/v1/swagger.json";
26+
string content = await GetAsync(requestUrl);
27+
28+
return JsonDocument.Parse(content);
29+
}, LazyThreadSafetyMode.ExecutionAndPublication);
30+
}
31+
32+
private async Task<string> GetAsync(string requestUrl)
33+
{
34+
var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
35+
36+
using HttpClient client = _testContext.Factory.CreateClient();
37+
HttpResponseMessage responseMessage = await client.SendAsync(request);
38+
39+
return await responseMessage.Content.ReadAsStringAsync();
40+
}
41+
42+
[Fact]
43+
public async Task Kebab_naming_policy_is_applied_to_get_collection_endpoint()
44+
{
45+
// Arrange
46+
const string expectedReferenceIdForResponseDocument = "supermarket-collection-response-document";
47+
const string expectedReferenceIdForToOneResourceResponseData = "to-one-staff-member-response-data";
48+
const string expectedReferenceIdForToManyResourceResponseData = "to-many-staff-member-response-data";
49+
const string expectedReferenceIdForResourceDataInResponse = "supermarket-data-in-response";
50+
const string expectedReferenceIdForResourceIdentifier = "staff-member-identifier";
51+
const string expectedReferenceIdResourceAttributesInResponse = "supermarket-attributes-in-response";
52+
const string expectedReferenceIdResourceRelationshipsInResponse = "supermarket-relationships-in-response";
53+
const string expectedReferenceIdForLinksInRelationshipObject = "links-in-relationship-object";
54+
const string expectedReferenceIdForLinksResourceObject = "links-in-resource-object";
55+
const string expectedReferenceIdForTopLevelLinks = "links-in-resource-collection-document";
56+
const string expectedReferenceIdForJsonapiObject = "jsonapi-object";
57+
const string expectedReferenceIdForNullValue = "null-value";
58+
const string expectedReferenceIdForEnum = "supermarket-type";
59+
60+
const string expectedOperationId = "get-supermarket-collection";
61+
const string expectedPrimaryResourcePublicName = "supermarkets";
62+
string expectedPrimaryResourceType = $"{expectedPrimaryResourcePublicName}-resource-type";
63+
string expectedPath = $"/{expectedPrimaryResourcePublicName}";
64+
const string expectedPropertyNameForStringAttribute = "name-of-city";
65+
const string expectedPropertyNameForEnumAttribute = "kind";
66+
const string expectedPropertyNameForToOneRelationship = "store-manager";
67+
const string expectedPropertyNameForToManyRelationship = "cashiers";
68+
const string expectedRelatedResourceType = "staff-members-resource-type";
69+
const string expectedRelatedResourcePublicName = "staff-members";
70+
71+
// Act
72+
JsonDocument document = await _lazyOpenApiDocument.Value;
73+
74+
// Assert
75+
document.SelectTokenOrError("paths").Should().HaveProperty(expectedPath);
76+
document.SelectTokenOrError($"paths.{expectedPath}.get.operationId").GetString().Should().Be(expectedOperationId);
77+
78+
string responseRefId = document.SelectTokenOrError($"paths.{expectedPath}.get.responses.200.content['application/vnd.api+json'].schema.$ref")
79+
.GetReferenceSchemaId();
80+
81+
responseRefId.Should().Be(expectedReferenceIdForResponseDocument);
82+
83+
string topLevelLinksRefId = document.SelectTokenOrError($"components.schemas.{responseRefId}.properties.links.$ref").GetReferenceSchemaId();
84+
topLevelLinksRefId.Should().Be(expectedReferenceIdForTopLevelLinks);
85+
86+
string jsonapiRefId = document.SelectTokenOrError($"components.schemas.{responseRefId}.properties.jsonapi.$ref").GetReferenceSchemaId();
87+
jsonapiRefId.Should().Be(expectedReferenceIdForJsonapiObject);
88+
89+
string dataRefId = document.SelectTokenOrError($"components.schemas.{responseRefId}.properties.data.items.$ref").GetReferenceSchemaId();
90+
dataRefId.Should().Be(expectedReferenceIdForResourceDataInResponse);
91+
92+
string resourceLinksRefId = document.SelectTokenOrError($"components.schemas.{dataRefId}.properties.links.$ref").GetReferenceSchemaId();
93+
resourceLinksRefId.Should().Be(expectedReferenceIdForLinksResourceObject);
94+
95+
string primaryResourceTypeRefId = document.SelectTokenOrError($"components.schemas.{dataRefId}.properties.type.$ref").GetReferenceSchemaId();
96+
primaryResourceTypeRefId.Should().Be(expectedPrimaryResourceType);
97+
string primaryResourceTypeValue = document.SelectTokenOrError($"components.schemas.{primaryResourceTypeRefId}.enum[0]").GetString();
98+
primaryResourceTypeValue.Should().Be(expectedPrimaryResourcePublicName);
99+
100+
string attributesRefId = document.SelectTokenOrError($"components.schemas.{dataRefId}.properties.attributes.$ref").GetReferenceSchemaId();
101+
attributesRefId.Should().Be(expectedReferenceIdResourceAttributesInResponse);
102+
JsonElement attributes = document.SelectTokenOrError($"components.schemas.{attributesRefId}.properties");
103+
104+
attributes.Should().HaveProperty(expectedPropertyNameForStringAttribute);
105+
attributes.Should().HaveProperty(expectedPropertyNameForEnumAttribute);
106+
string enumRefId = document.SelectTokenOrError($"components.schemas.{attributesRefId}.properties.kind.$ref").GetReferenceSchemaId();
107+
enumRefId.Should().Be(expectedReferenceIdForEnum);
108+
109+
string relationshipsRefId = document.SelectTokenOrError($"components.schemas.{dataRefId}.properties.relationships.$ref").GetReferenceSchemaId();
110+
relationshipsRefId.Should().Be(expectedReferenceIdResourceRelationshipsInResponse);
111+
112+
JsonElement relationships = document.SelectTokenOrError($"components.schemas.{relationshipsRefId}.properties");
113+
relationships.Should().HaveProperty(expectedPropertyNameForToOneRelationship);
114+
115+
string toOneResourceRefId = document.SelectTokenOrError($"components.schemas.{relationshipsRefId}.properties.store-manager.$ref")
116+
.GetReferenceSchemaId();
117+
118+
toOneResourceRefId.Should().Be(expectedReferenceIdForToOneResourceResponseData);
119+
relationships.Should().HaveProperty(expectedPropertyNameForToManyRelationship);
120+
string toManyResourceRefId = document.SelectTokenOrError($"components.schemas.{relationshipsRefId}.properties.cashiers.$ref").GetReferenceSchemaId();
121+
toManyResourceRefId.Should().Be(expectedReferenceIdForToManyResourceResponseData);
122+
123+
string relationshipLinksRefId = document.SelectTokenOrError($"components.schemas.{toOneResourceRefId}.properties.links.$ref").GetReferenceSchemaId();
124+
relationshipLinksRefId.Should().Be(expectedReferenceIdForLinksInRelationshipObject);
125+
126+
string resourceIdentifierRefId = document.SelectTokenOrError($"components.schemas.{toOneResourceRefId}.properties.data.oneOf[0].$ref")
127+
.GetReferenceSchemaId();
128+
129+
resourceIdentifierRefId.Should().Be(expectedReferenceIdForResourceIdentifier);
130+
string nullValueRefId = document.SelectTokenOrError($"components.schemas.{toOneResourceRefId}.properties.data.oneOf[1].$ref").GetReferenceSchemaId();
131+
nullValueRefId.Should().Be(expectedReferenceIdForNullValue);
132+
133+
string relatedResourceTypeReferenceSchema =
134+
document.SelectTokenOrError($"components.schemas.{resourceIdentifierRefId}.properties.type.$ref").GetReferenceSchemaId();
135+
136+
relatedResourceTypeReferenceSchema.Should().Be(expectedRelatedResourceType);
137+
138+
string relatedResourceTypeValue =
139+
document.SelectTokenOrError($"components.schemas.{relatedResourceTypeReferenceSchema}.enum[0]").GetReferenceSchemaId();
140+
141+
relatedResourceTypeValue.Should().Be(expectedRelatedResourcePublicName);
142+
}
143+
144+
[Fact]
145+
public async Task Kebab_naming_policy_is_applied_to_get_single_endpoint()
146+
{
147+
// Arrange
148+
const string expectedReferenceIdForResponseDocument = "supermarket-primary-response-document";
149+
const string expectedReferenceIdForTopLevelLinks = "links-in-resource-document";
150+
151+
const string expectedOperationId = "get-supermarket";
152+
const string expectedPrimaryResourcePublicName = "supermarkets";
153+
string expectedPath = $"/{expectedPrimaryResourcePublicName}/{{id}}";
154+
155+
// Act
156+
JsonDocument document = await _lazyOpenApiDocument.Value;
157+
158+
// Assert
159+
document.SelectTokenOrError("paths").Should().HaveProperty(expectedPath);
160+
document.SelectTokenOrError($"paths.{expectedPath}.get.tags[0]").GetString().Should().Be(expectedPrimaryResourcePublicName);
161+
document.SelectTokenOrError($"paths.{expectedPath}.get.operationId").GetString().Should().Be(expectedOperationId);
162+
163+
string responseRefId = document.SelectTokenOrError($"paths.{expectedPath}.get.responses.200.content['application/vnd.api+json'].schema.$ref")
164+
.GetReferenceSchemaId();
165+
166+
responseRefId.Should().Be(expectedReferenceIdForResponseDocument);
167+
168+
string topLevelLinksRefId = document.SelectTokenOrError($"components.schemas.{responseRefId}.properties.links.$ref").GetReferenceSchemaId();
169+
topLevelLinksRefId.Should().Be(expectedReferenceIdForTopLevelLinks);
170+
}
171+
}
172+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using JetBrains.Annotations;
2+
using Microsoft.EntityFrameworkCore;
3+
4+
// @formatter:wrap_chained_method_calls chop_always
5+
// @formatter:keep_existing_linebreaks true
6+
7+
namespace OpenApiTests.NamingConvention
8+
{
9+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
10+
public sealed class NamingConventionDbContext : DbContext
11+
{
12+
public DbSet<Supermarket> Supermarkets { get; set; }
13+
public DbSet<StaffMember> StaffMembers { get; set; }
14+
15+
public NamingConventionDbContext(DbContextOptions<NamingConventionDbContext> options)
16+
: base(options)
17+
{
18+
}
19+
}
20+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using JetBrains.Annotations;
2+
using JsonApiDotNetCore.Resources;
3+
using JsonApiDotNetCore.Resources.Annotations;
4+
5+
namespace OpenApiTests.NamingConvention
6+
{
7+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8+
public sealed class StaffMember : Identifiable
9+
{
10+
[Attr]
11+
public string Name { get; set; }
12+
13+
[Attr]
14+
public int Age { get; set; }
15+
}
16+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Collections.Generic;
2+
using JetBrains.Annotations;
3+
using JsonApiDotNetCore.Resources;
4+
using JsonApiDotNetCore.Resources.Annotations;
5+
6+
namespace OpenApiTests.NamingConvention
7+
{
8+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9+
public sealed class Supermarket : Identifiable
10+
{
11+
[Attr]
12+
public string NameOfCity { get; set; }
13+
14+
[Attr]
15+
public SupermarketType Kind { get; set; }
16+
17+
[HasOne]
18+
public StaffMember StoreManager { get; set; }
19+
20+
[HasMany]
21+
public ICollection<StaffMember> Cashiers { get; set; }
22+
}
23+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace OpenApiTests.NamingConvention
2+
{
3+
public enum SupermarketType
4+
{
5+
Traditional,
6+
Budget,
7+
Warehouse
8+
}
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.Services;
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace OpenApiTests.NamingConvention
7+
{
8+
public sealed class SupermarketsController : JsonApiController<Supermarket>
9+
{
10+
public SupermarketsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService<Supermarket> resourceService)
11+
: base(options, loggerFactory, resourceService)
12+
{
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)