Skip to content

Commit 32dba78

Browse files
Build workflow with tests, sonar workflow, snyk workflow, added new CONTRIBUTING. (#40)
* Added build, snyk & sonarcloud workflow. * Added tests. * Added new CONTRIBUTING.md.
1 parent c76e11b commit 32dba78

File tree

13 files changed

+4782
-23
lines changed

13 files changed

+4782
-23
lines changed

.github/workflows/build.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: .NET build
2+
3+
on: [push]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
matrix:
10+
dotnet-version: [6.x, 7.x, 8.x]
11+
steps:
12+
- uses: actions/checkout@v4
13+
- name: Setup .NET
14+
uses: actions/setup-dotnet@v4
15+
with:
16+
dotnet-version: ${{ matrix.dotnet-version }}
17+
- name: Restore dependencies
18+
run: dotnet restore
19+
- name: Build
20+
run: dotnet build --no-restore
21+
- name: Test
22+
run: dotnet test --no-build --verbosity normal

.github/workflows/snyk.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Snyk vulenrability scan
2+
3+
on: [push]
4+
jobs:
5+
security:
6+
runs-on: ubuntu-latest
7+
steps:
8+
- uses: actions/checkout@master
9+
- name: Setup .NET
10+
uses: actions/setup-dotnet@v3
11+
with:
12+
dotnet-version: '6.x'
13+
- name: Restore dependencies
14+
run: dotnet restore
15+
- name: Run Snyk to check for vulnerabilities
16+
uses: snyk/actions/dotnet@master
17+
continue-on-error: true # To make sure that SARIF upload gets called
18+
env:
19+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
20+
with:
21+
args:
22+
--all-projects
23+
--sarif-file-output=snyk.sarif
24+
--severity-threshold=high
25+
- name: Upload result to GitHub Code Scanning
26+
uses: github/codeql-action/upload-sarif@v3
27+
with:
28+
sarif_file: snyk.sarif

.github/workflows/sonarcloud.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: SonarCloud analysis
2+
3+
on: [push]
4+
5+
jobs:
6+
sonarcloud:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- name: Checkout code
10+
uses: actions/checkout@v4
11+
- name: Setup .NET
12+
uses: actions/setup-dotnet@v4
13+
with:
14+
dotnet-version: '6.x'
15+
- name: Restore dependencies
16+
run: dotnet restore
17+
- name: Build
18+
run: dotnet build --no-restore
19+
- name: Install SonarScanner for .NET
20+
run: dotnet tool install --global dotnet-sonarscanner
21+
- name: Run SonarCloud Scan
22+
env:
23+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
24+
run: |
25+
dotnet-sonarscanner begin /k:"infobip_infobip-api-csharp-client" /o:"infobip" /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
26+
dotnet build
27+
dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"

ApiClient.Tests/Api/ApiTest.cs

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
using System.Net;
2+
using Infobip.Api.Client;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
using WireMock.Matchers;
5+
using WireMock.RequestBuilders;
6+
using WireMock.ResponseBuilders;
7+
using WireMock.Server;
8+
using WireMock.Types;
9+
10+
namespace ApiClient.Tests.Api;
11+
12+
public class ApiTest
13+
{
14+
protected const string API_KEY_PREFIX = "App";
15+
protected const string API_KEY = "003026bbc133714df1834b8638bb496e-8f4b3d9a-e931-478d-a994-28a725159ab9";
16+
protected const string API_KEY_HEADER_VALUE = API_KEY_PREFIX + " " + API_KEY;
17+
protected const string CONTENT_TYPE_HEADER_VALUE = "application/json; charset=utf-8";
18+
protected const string USER_AGENT_HEADER_VALUE = "infobip-api-client-csharp/" + Configuration.Version;
19+
protected const string ACCEPT_HEADER_VALUE = "application/json";
20+
protected const string SERVER_HEADER_VALUE = "SMS API";
21+
protected const string SERVER_HEADER_VALUE_COMMA = "SMS,API";
22+
protected const string X_REQUEST_ID_HEADER_VALUE = "1608758729810312842";
23+
24+
protected Configuration? configuration;
25+
26+
protected WireMockServer? wireMockServer;
27+
28+
[TestInitialize]
29+
public void StartMockServer()
30+
{
31+
wireMockServer = WireMockServer.Start();
32+
33+
configuration = new Configuration
34+
{
35+
ApiKey = API_KEY,
36+
BasePath = "http://localhost:" + wireMockServer.Ports[0]
37+
};
38+
}
39+
40+
[TestCleanup]
41+
public void TearDown()
42+
{
43+
wireMockServer!.Stop();
44+
}
45+
46+
protected void SetUpGetRequest(string url, string expectedResponse, int statusCode)
47+
{
48+
SetUpGetRequest(url, new Dictionary<string, string>(), expectedResponse, statusCode);
49+
}
50+
51+
protected void SetUpPostRequest(string url, string givenRequest, string expectedResponse, int statusCode)
52+
{
53+
SetUpPostRequest(url, new Dictionary<string, string>(), givenRequest, expectedResponse, statusCode);
54+
}
55+
56+
protected void SetUpPutRequest(string url, string givenRequest, string expectedResponse, int statusCode)
57+
{
58+
SetUpPutRequest(url, new Dictionary<string, string>(), givenRequest, expectedResponse, statusCode);
59+
}
60+
61+
protected void SetUpDeleteRequest(string url, int statusCode)
62+
{
63+
SetUpDeleteRequest(url, new Dictionary<string, string>(), statusCode);
64+
}
65+
66+
protected void SetUpGetRequest(string url, Dictionary<string, string> givenParameters, string expectedResponse,
67+
int statusCode)
68+
{
69+
wireMockServer!.Given(Request.Create().UsingGet().WithPath(url)
70+
.WithHeader("Authorization", new ExactMatcher(API_KEY_HEADER_VALUE))
71+
.WithHeader("Accept", new ExactMatcher(ACCEPT_HEADER_VALUE))
72+
.WithHeader("User-Agent", new ExactMatcher(USER_AGENT_HEADER_VALUE))
73+
.WithParam(EqualToParams(givenParameters))
74+
)
75+
.RespondWith(Response.Create()
76+
.WithStatusCode(statusCode)
77+
.WithHeader("Content-Type", CONTENT_TYPE_HEADER_VALUE)
78+
.WithHeader("Server", SERVER_HEADER_VALUE)
79+
.WithHeader("X-Request-Id", X_REQUEST_ID_HEADER_VALUE)
80+
.WithBody(expectedResponse)
81+
);
82+
}
83+
84+
protected void SetUpPostRequest(string url, Dictionary<string, string> givenParameters, string givenRequest,
85+
string expectedResponse, int statusCode)
86+
{
87+
wireMockServer!.Given(Request.Create().UsingPost().WithPath(url)
88+
.WithHeader("Authorization", new ExactMatcher(API_KEY_HEADER_VALUE))
89+
.WithHeader("Content-Type", new ExactMatcher(CONTENT_TYPE_HEADER_VALUE))
90+
.WithHeader("Accept", new ExactMatcher(ACCEPT_HEADER_VALUE))
91+
.WithHeader("User-Agent", new ExactMatcher(USER_AGENT_HEADER_VALUE))
92+
.WithParam(EqualToParams(givenParameters))
93+
.WithBody(new JsonMatcher(givenRequest, true))
94+
)
95+
.RespondWith(Response.Create()
96+
.WithStatusCode(statusCode)
97+
.WithHeader("Content-Type", CONTENT_TYPE_HEADER_VALUE)
98+
.WithHeader("Server", SERVER_HEADER_VALUE)
99+
.WithHeader("X-Request-Id", X_REQUEST_ID_HEADER_VALUE)
100+
.WithBody(expectedResponse)
101+
);
102+
}
103+
104+
protected void SetUpPostRequestBinary(string url, Dictionary<string, string> givenParameters, byte[] givenRequest,
105+
string expectedResponse, int statusCode)
106+
{
107+
wireMockServer!.Given(Request.Create().UsingPost().WithPath(url)
108+
.WithHeader("Authorization", new ExactMatcher(API_KEY_HEADER_VALUE))
109+
.WithHeader("Content-Type", new ExactMatcher("application/octet-stream"))
110+
.WithHeader("Accept", new ExactMatcher(ACCEPT_HEADER_VALUE))
111+
.WithHeader("User-Agent", new ExactMatcher(USER_AGENT_HEADER_VALUE))
112+
.WithParam(EqualToParams(givenParameters))
113+
.WithBody(givenRequest)
114+
)
115+
.RespondWith(Response.Create()
116+
.WithStatusCode(statusCode)
117+
.WithHeader("Content-Type", CONTENT_TYPE_HEADER_VALUE)
118+
.WithHeader("Server", SERVER_HEADER_VALUE)
119+
.WithHeader("X-Request-Id", X_REQUEST_ID_HEADER_VALUE)
120+
.WithBody(expectedResponse)
121+
);
122+
}
123+
124+
protected void SetUpNoRequestBodyNoResponseBodyPostRequest(string url, int statusCode)
125+
{
126+
wireMockServer!.Given(Request.Create().UsingPost().WithPath(url)
127+
.WithHeader("Authorization", new ExactMatcher(API_KEY_HEADER_VALUE))
128+
.WithHeader("Accept", new ExactMatcher(ACCEPT_HEADER_VALUE))
129+
.WithHeader("User-Agent", new ExactMatcher(USER_AGENT_HEADER_VALUE))
130+
)
131+
.RespondWith(Response.Create()
132+
.WithStatusCode(statusCode)
133+
.WithHeader("Server", SERVER_HEADER_VALUE)
134+
.WithHeader("X-Request-Id", X_REQUEST_ID_HEADER_VALUE)
135+
);
136+
}
137+
138+
protected void SetUpPutRequest(string url, Dictionary<string, string> givenParameters, string givenRequest,
139+
string expectedResponse, int statusCode)
140+
{
141+
wireMockServer!.Given(Request.Create().UsingPut().WithPath(url)
142+
.WithHeader("Authorization", new ExactMatcher(API_KEY_HEADER_VALUE))
143+
.WithHeader("Content-Type", new ExactMatcher(CONTENT_TYPE_HEADER_VALUE))
144+
.WithHeader("Accept", new ExactMatcher(ACCEPT_HEADER_VALUE))
145+
.WithHeader("User-Agent", new ExactMatcher(USER_AGENT_HEADER_VALUE))
146+
.WithParam(EqualToParams(givenParameters))
147+
.WithBody(new JsonMatcher(givenRequest, true))
148+
)
149+
.RespondWith(Response.Create()
150+
.WithStatusCode(statusCode)
151+
.WithHeader("Content-Type", CONTENT_TYPE_HEADER_VALUE)
152+
.WithHeader("Server", SERVER_HEADER_VALUE)
153+
.WithHeader("X-Request-Id", X_REQUEST_ID_HEADER_VALUE)
154+
.WithBody(expectedResponse)
155+
);
156+
}
157+
158+
protected void SetUpDeleteRequest(string url, Dictionary<string, string> givenParameters, int statusCode)
159+
{
160+
wireMockServer!.Given(Request.Create().UsingDelete().WithPath(url)
161+
.WithHeader("Authorization", new ExactMatcher(API_KEY_HEADER_VALUE))
162+
.WithHeader("Accept", new ExactMatcher(ACCEPT_HEADER_VALUE))
163+
.WithParam(EqualToParams(givenParameters))
164+
)
165+
.RespondWith(Response.Create()
166+
.WithStatusCode(statusCode)
167+
.WithHeader("Server", SERVER_HEADER_VALUE)
168+
.WithHeader("X-Request-Id", X_REQUEST_ID_HEADER_VALUE)
169+
);
170+
}
171+
172+
protected void SetUpDeleteRequestWithResponseBody(string url, Dictionary<string, string> givenParameters,
173+
string expectedResponse, int statusCode)
174+
{
175+
wireMockServer!.Given(Request.Create().UsingDelete().WithPath(url)
176+
.WithHeader("Authorization", new ExactMatcher(API_KEY_HEADER_VALUE))
177+
.WithHeader("Accept", new ExactMatcher(ACCEPT_HEADER_VALUE))
178+
.WithParam(EqualToParams(givenParameters))
179+
)
180+
.RespondWith(Response.Create()
181+
.WithStatusCode(statusCode)
182+
.WithHeader("Server", SERVER_HEADER_VALUE)
183+
.WithHeader("X-Request-Id", X_REQUEST_ID_HEADER_VALUE)
184+
.WithHeader("Content-Type", CONTENT_TYPE_HEADER_VALUE)
185+
.WithBody(expectedResponse)
186+
);
187+
}
188+
189+
protected void SetUpMultipartFormRequest(string url, Multimap<string, string> givenParts, string expectedResponse,
190+
int statusCode = 200)
191+
{
192+
var req = Request.Create().UsingPost().WithPath(url)
193+
.WithHeader("Authorization", new ExactMatcher(API_KEY_HEADER_VALUE))
194+
.WithHeader("Content-Type", new WildcardMatcher("multipart/form-data; boundary=\"---------*", true))
195+
.WithHeader("Accept", new ExactMatcher(ACCEPT_HEADER_VALUE))
196+
.WithHeader("User-Agent", new ExactMatcher(USER_AGENT_HEADER_VALUE));
197+
198+
req.WithBody(body =>
199+
{
200+
var allKeysFound = givenParts.All(kvp =>
201+
body.Contains($"name={kvp.Key}", StringComparison.InvariantCultureIgnoreCase));
202+
var allValuesFound = givenParts.All(kvp =>
203+
kvp.Value.All(value => body.Contains(value, StringComparison.InvariantCultureIgnoreCase)));
204+
return allValuesFound && allKeysFound;
205+
});
206+
207+
var resp = Response.Create()
208+
.WithStatusCode(statusCode)
209+
.WithHeader("Content-Type", CONTENT_TYPE_HEADER_VALUE)
210+
.WithHeader("Server", SERVER_HEADER_VALUE)
211+
.WithHeader("X-Request-Id", X_REQUEST_ID_HEADER_VALUE)
212+
.WithBody(expectedResponse);
213+
214+
wireMockServer!.Given(req).RespondWith(resp);
215+
}
216+
217+
private Func<IDictionary<string, WireMockList<string>>, bool>[] EqualToParams(Dictionary<string, string> parameters)
218+
{
219+
var funcs = new List<Func<IDictionary<string, WireMockList<string>>, bool>>();
220+
foreach (var param in parameters)
221+
funcs.Add(delegate(IDictionary<string, WireMockList<string>> inputParams)
222+
{
223+
return inputParams[param.Key][0] == param.Value;
224+
});
225+
if (funcs.Count == 0) funcs.Add(x => true);
226+
return funcs.ToArray();
227+
}
228+
229+
public static void AssertResponse<T>(T apiResponse, Action<T> assertion)
230+
{
231+
assertion.Invoke(apiResponse);
232+
}
233+
234+
public static void AssertResponseWithHttpInfo<T>(ApiResponse<T> apiResponse, Action<T> assertion)
235+
{
236+
Assert.AreEqual(HttpStatusCode.OK, apiResponse.StatusCode);
237+
238+
Assert.AreEqual(SERVER_HEADER_VALUE_COMMA, apiResponse.Headers["Server"][0]);
239+
Assert.AreEqual(X_REQUEST_ID_HEADER_VALUE, apiResponse.Headers["X-Request-ID"][0]);
240+
Assert.AreEqual(CONTENT_TYPE_HEADER_VALUE, apiResponse.Headers["Content-Type"][0]);
241+
242+
assertion.Invoke(apiResponse.Data);
243+
}
244+
245+
public static void AssertNoBodyResponseWithHttpInfo<T>(ApiResponse<T> apiResponse,
246+
HttpStatusCode expectedHttpStatusCode)
247+
{
248+
Assert.AreEqual(expectedHttpStatusCode, apiResponse.StatusCode);
249+
250+
Assert.AreEqual(SERVER_HEADER_VALUE_COMMA, apiResponse.Headers["Server"][0]);
251+
Assert.AreEqual(X_REQUEST_ID_HEADER_VALUE, apiResponse.Headers["X-Request-ID"][0]);
252+
}
253+
}

0 commit comments

Comments
 (0)