-
-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #79 from bgmulinari/2.0.0
2.0.0
- Loading branch information
Showing
26 changed files
with
2,607 additions
and
1,924 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>disable</Nullable> | ||
<IsPackable>false</IsPackable> | ||
<IsTestProject>true</IsTestProject> | ||
<LangVersion>12</LangVersion> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" /> | ||
<PackageReference Include="xunit" Version="2.9.0" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\B1SLayer\B1SLayer.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using Flurl.Http.Content; | ||
using Flurl.Http.Testing; | ||
|
||
namespace B1SLayer.Test; | ||
|
||
internal static class FlurlExtensions | ||
{ | ||
public static HttpCallAssertion WithRequestMultipart(this HttpCallAssertion assertion, Func<CapturedStringContent, bool> predicate) | ||
{ | ||
return assertion.With(x => | ||
{ | ||
string content = x.HttpRequestMessage.Content.ReadAsStringAsync().Result; | ||
return predicate(new CapturedStringContent(content, "CapturedMultipartContent body")); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
global using Xunit; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace B1SLayer.Test.Models; | ||
|
||
internal class MarketingDocument | ||
{ | ||
public int DocEntry { get; set; } | ||
public string CardCode { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using Flurl.Http.Testing; | ||
|
||
namespace B1SLayer.Test; | ||
|
||
public abstract class TestBase : IDisposable | ||
{ | ||
protected HttpTest HttpTest { get; private set; } | ||
protected static SLConnection SLConnectionV1 { get; } = new SLConnection("https://sapserver:50000/b1s/v1", "CompanyDB", "manager", "12345"); | ||
protected static SLConnection SLConnectionV2 { get; } = new SLConnection("https://sapserver:50000/b1s/v2", "CompanyDB", "manager", "12345"); | ||
protected static SLLoginResponse LoginResponse { get; } = new() { SessionId = "00000000-0000-0000-0000-000000000000", Version = "1000000", SessionTimeout = 30 }; | ||
|
||
public TestBase() | ||
{ | ||
HttpTest = new HttpTest(); | ||
|
||
// standard Login response | ||
HttpTest.ForCallsTo("*/b1s/v*/Login").RespondWithJson(LoginResponse); | ||
} | ||
|
||
public static IEnumerable<object[]> SLConnections() | ||
{ | ||
yield return new SLConnection[] { SLConnectionV1 }; | ||
yield return new SLConnection[] { SLConnectionV2 }; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
HttpTest.Dispose(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
using Flurl; | ||
using Flurl.Http; | ||
using Flurl.Http.Testing; | ||
using System.IO.Enumeration; | ||
using System.Net; | ||
|
||
namespace B1SLayer.Test; | ||
|
||
public class SLBatchRequestTests : TestBase | ||
{ | ||
private const string | ||
expectedRequestBody = "*--*-*-*-*-*Content-Type: multipart/mixed; boundary=\"changeset_*-*-*-*-*\"*--changeset_*-*-*-*-*Content-Type: application/http; msgtype=request*content-transfer-encoding: binary*Content-ID: 1*POST /b1s/v*/BusinessPartners HTTP/1.1*Host: *:50000*Content-Type: application/json; charset=utf-8*{\"CardCode\":\"C00001\",\"CardName\":\"I am a new BP\"}*--changeset_*-*-*-*-*Content-Type: application/http; msgtype=request*content-transfer-encoding: binary*Content-ID: 2*PATCH /b1s/v*/BusinessPartners('C00001') HTTP/1.1*Host: *:50000*Content-Type: application/json; charset=utf-8*{\"CardName\":\"This is my updated name\"}*--changeset_*-*-*-*-*Content-Type: application/http; msgtype=request*content-transfer-encoding: binary*Content-ID: 3*DELETE /b1s/v*/BusinessPartners('C00001') HTTP/1.1*Host: *:50000*--changeset_*-*-*-*-*--*--*-*-*-*-*--*", | ||
v1Response = "--batchresponse_00000000-0000-0000-0000-000000000000\r\nContent-Type: multipart/mixed; boundary=changesetresponse_00000000-0000-0000-0000-000000000000\r\n\r\n--changesetresponse_00000000-0000-0000-0000-000000000000\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 201 Created\r\nContent-ID: 1\r\nContent-Type: application/json;odata=minimalmetadata;charset=utf-8\r\nContent-Length: 8811\r\nDataServiceVersion: 3.0\r\nETag: W/\"0000000000000000000000000000000000000000\"\r\nLocation: https://localhost:50000/b1s/v1/BusinessPartners('C00001')\r\n\r\n{\"some\":\"content\"}\n\r\n--changesetresponse_00000000-0000-0000-0000-000000000000\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nContent-ID: 2\r\nDataServiceVersion: 3.0\r\n\r\n\r\n--changesetresponse_00000000-0000-0000-0000-000000000000\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nContent-ID: 3\r\nDataServiceVersion: 3.0\r\n\r\n\r\n--changesetresponse_00000000-0000-0000-0000-000000000000--\r\n--batchresponse_00000000-0000-0000-0000-000000000000--\r\n", | ||
v2Response = "--batchresponse_00000000-0000-0000-0000-000000000000\r\nContent-Type: multipart/mixed; boundary=changesetresponse_00000000-0000-0000-0000-000000000000\r\n\r\n--changesetresponse_00000000-0000-0000-0000-000000000000\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\nContent-ID: 1\r\n\r\nHTTP/1.1 201 Created\r\nContent-Type: application/json;odata.metadata=minimal;charset=utf-8\r\nContent-Length: 8811\r\nETag: W/\"0000000000000000000000000000000000000000\"\r\nLocation: https://localhost:50000/b1s/v2/BusinessPartners('C00001')\r\nOData-Version: 4.0\r\n\r\n{\"some\":\"content\"}\n\r\n--changesetresponse_00000000-0000-0000-0000-000000000000\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\nContent-ID: 2\r\n\r\nHTTP/1.1 204 No Content\r\nOData-Version: 4.0\r\n\r\n\r\n--changesetresponse_00000000-0000-0000-0000-000000000000\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\nContent-ID: 3\r\n\r\nHTTP/1.1 204 No Content\r\nOData-Version: 4.0\r\n\r\n\r\n--changesetresponse_00000000-0000-0000-0000-000000000000--\r\n--batchresponse_00000000-0000-0000-0000-000000000000--\r\n"; | ||
|
||
private static readonly SLBatchRequest[] _requests = | ||
[ | ||
new SLBatchRequest(HttpMethod.Post, "BusinessPartners", new { CardCode = "C00001", CardName = "I am a new BP" }, 1), | ||
new SLBatchRequest(HttpMethod.Patch, "BusinessPartners('C00001')", new { CardName = "This is my updated name" }, 2), | ||
new SLBatchRequest(HttpMethod.Delete, "BusinessPartners('C00001')", contentID: 3) | ||
]; | ||
|
||
[Fact] | ||
public async Task PostBatchAsyncV1_ReturnsCorrectData() | ||
{ | ||
HttpTest.RespondWith( | ||
body: v1Response, | ||
status: 202, | ||
headers: new Dictionary<string, string> { { "Content-Type", "multipart/mixed;boundary=batchresponse_00000000-0000-0000-0000-000000000000" } }); | ||
|
||
var batchResult = await SLConnectionV1.PostBatchAsync(_requests); | ||
|
||
HttpTest.ShouldHaveCalled(SLConnectionV1.ServiceLayerRoot.AppendPathSegment("$batch")) | ||
.WithVerb(HttpMethod.Post) | ||
.WithRequestMultipart(call => FileSystemName.MatchesSimpleExpression(expectedRequestBody, call.Content)) | ||
.Times(1); | ||
|
||
Assert.Equal(3, batchResult.Length); | ||
Assert.Equal(HttpStatusCode.Created, batchResult[0].StatusCode); | ||
Assert.Equal(HttpStatusCode.NoContent, batchResult[1].StatusCode); | ||
Assert.Equal(HttpStatusCode.NoContent, batchResult[2].StatusCode); | ||
Assert.Equal("{\"some\":\"content\"}", await batchResult[0].Content.ReadAsStringAsync()); | ||
} | ||
|
||
[Fact] | ||
public async Task PostBatchAsyncV2_ReturnsCorrectData() | ||
{ | ||
HttpTest.RespondWith( | ||
body: v2Response, | ||
status: 202, | ||
headers: new Dictionary<string, string> { { "Content-Type", "multipart/mixed;boundary=batchresponse_00000000-0000-0000-0000-000000000000" } }); | ||
|
||
var batchResult = await SLConnectionV2.PostBatchAsync(_requests); | ||
|
||
HttpTest.ShouldHaveCalled(SLConnectionV2.ServiceLayerRoot.AppendPathSegment("$batch")) | ||
.WithVerb(HttpMethod.Post) | ||
.WithRequestMultipart(call => FileSystemName.MatchesSimpleExpression(expectedRequestBody, call.Content)) | ||
.Times(1); | ||
|
||
Assert.Equal(3, batchResult.Length); | ||
Assert.Equal(HttpStatusCode.Created, batchResult[0].StatusCode); | ||
Assert.Equal(HttpStatusCode.NoContent, batchResult[1].StatusCode); | ||
Assert.Equal(HttpStatusCode.NoContent, batchResult[2].StatusCode); | ||
Assert.Equal("{\"some\":\"content\"}", await batchResult[0].Content.ReadAsStringAsync()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
using B1SLayer.Models; | ||
using B1SLayer.Test.Models; | ||
using Flurl; | ||
using Flurl.Http.Testing; | ||
|
||
namespace B1SLayer.Test; | ||
|
||
public class SLRequestTests : TestBase | ||
{ | ||
[Theory] | ||
[MemberData(nameof(SLConnections))] | ||
public async Task RequestParameters_AreApplied(SLConnection slConnection) | ||
{ | ||
HttpTest.RespondWith("{}"); | ||
|
||
await slConnection.Request("$crossjoin(Orders,Orders/DocumentLines)") | ||
.Select("DocEntry,CardCode") | ||
.Expand("Orders/DocumentLines($select=ItemCode,LineNum)") | ||
.Filter("Orders/DocEntry eq Orders/DocumentLines/DocEntry") | ||
.OrderBy("DocTotal asc,DocEntry desc") | ||
.Apply("aggregate(DocRate with sum as TotalDocRate)") | ||
.Top(1) | ||
.Skip(2) | ||
.SetQueryParam("X", "Z") | ||
.WithReturnNoContent() | ||
.WithCaseInsensitive() | ||
.WithReplaceCollectionsOnPatch() | ||
.WithPageSize(50) | ||
.WithHeader("A", "B") | ||
.GetAsync<object>(); | ||
|
||
HttpTest.ShouldHaveCalled(slConnection.ServiceLayerRoot.AppendPathSegment("$crossjoin(Orders,Orders/DocumentLines)")) | ||
.WithVerb(HttpMethod.Get) | ||
.WithQueryParam("$select", "DocEntry,CardCode") | ||
.WithQueryParam("$expand", "Orders/DocumentLines($select=ItemCode,LineNum)") | ||
.WithQueryParam("$filter", "Orders/DocEntry eq Orders/DocumentLines/DocEntry") | ||
.WithQueryParam("$orderby", "DocTotal asc,DocEntry desc") | ||
.WithQueryParam("$apply", "aggregate(DocRate with sum as TotalDocRate)") | ||
.WithQueryParam("$top", 1) | ||
.WithQueryParam("$skip", 2) | ||
.WithQueryParam("X", "Z") | ||
.WithHeader("Prefer", "return-no-content") | ||
.WithHeader("B1S-CaseInsensitive", "true") | ||
.WithHeader("B1S-ReplaceCollectionsOnPatch", "true") | ||
.WithHeader("B1S-PageSize", 50) | ||
.WithHeader("A", "B") | ||
.Times(1); | ||
} | ||
|
||
[Theory] | ||
[MemberData(nameof(SLConnections))] | ||
public async Task LoginAsync_IsPerformedAutomatically(SLConnection slConnection) | ||
{ | ||
await slConnection.Request("Orders").GetStringAsync(); // random request | ||
|
||
Assert.Equal(LoginResponse.SessionId, slConnection.LoginResponse.SessionId); | ||
Assert.Equal(LoginResponse.Version, slConnection.LoginResponse.Version); | ||
Assert.Equal(LoginResponse.SessionTimeout, slConnection.LoginResponse.SessionTimeout); | ||
|
||
await slConnection.LogoutAsync(); | ||
} | ||
|
||
[Theory] | ||
[MemberData(nameof(SLConnections))] | ||
public async Task GetAsync_ReturnsCorrectData(SLConnection slConnection) | ||
{ | ||
var expectedData = new List<MarketingDocument> | ||
{ | ||
new() { DocEntry = 1, CardCode = "C20001" }, | ||
new() { DocEntry = 2, CardCode = "C20002" } | ||
}; | ||
|
||
HttpTest.RespondWithJson(new SLCollectionRoot<MarketingDocument> { Value = expectedData }); | ||
|
||
var result = await slConnection | ||
.Request("Orders") | ||
.GetAsync<List<MarketingDocument>>(); | ||
|
||
HttpTest.ShouldHaveCalled(slConnection.ServiceLayerRoot.AppendPathSegment("Orders")) | ||
.WithVerb(HttpMethod.Get) | ||
.Times(1); | ||
|
||
Assert.Equal(2, result.Count); | ||
Assert.Equal(expectedData[0].DocEntry, result[0].DocEntry); | ||
Assert.Equal(expectedData[1].CardCode, result[1].CardCode); | ||
} | ||
|
||
[Theory] | ||
[MemberData(nameof(SLConnections))] | ||
public async Task GetAllAsync_ReturnsCorrectData(SLConnection slConnection) | ||
{ | ||
var page1 = new SLCollectionRoot<MarketingDocument> | ||
{ | ||
Value = | ||
[ | ||
new() { DocEntry = 1, CardCode = "C20001" }, | ||
new() { DocEntry = 2, CardCode = "C20002" } | ||
], | ||
ODataNextLinkJson = "Orders?$select=DocEntry,CardCode&$skip=2" | ||
}; | ||
|
||
var page2 = new SLCollectionRoot<MarketingDocument> | ||
{ | ||
Value = | ||
[ | ||
new() { DocEntry = 3, CardCode = "C20003" }, | ||
new() { DocEntry = 4, CardCode = "C20004" } | ||
], | ||
ODataNextLinkJson = "Orders?$select=DocEntry,CardCode&$skip=4" | ||
}; | ||
|
||
HttpTest.RespondWithJson(page1); | ||
HttpTest.RespondWithJson(page2); | ||
HttpTest.RespondWith("{\"value\":[]}"); | ||
|
||
var orderList = await slConnection.Request("Orders").WithPageSize(2).GetAllAsync<MarketingDocument>(); | ||
|
||
HttpTest.ShouldHaveCalled(slConnection.ServiceLayerRoot.AppendPathSegment("Orders")) | ||
.WithVerb(HttpMethod.Get) | ||
.Times(3); | ||
|
||
Assert.Equal(4, orderList.Count); | ||
Assert.Equal((page1.Value[0].DocEntry, page1.Value[0].CardCode), (orderList[0].DocEntry, orderList[0].CardCode)); | ||
Assert.Equal((page1.Value[1].DocEntry, page1.Value[1].CardCode), (orderList[1].DocEntry, orderList[1].CardCode)); | ||
Assert.Equal((page2.Value[0].DocEntry, page2.Value[0].CardCode), (orderList[2].DocEntry, orderList[2].CardCode)); | ||
Assert.Equal((page2.Value[1].DocEntry, page2.Value[1].CardCode), (orderList[3].DocEntry, orderList[3].CardCode)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.