mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
Edit auth bdd tests
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
Feature: User Login
|
Feature: User Login
|
||||||
As a registered user
|
As a registered user
|
||||||
I want to log in to my account
|
I want to log in to my account
|
||||||
So that I receive an authentication token to access authenticated routes
|
So that I receive an authentication token to access authenticated routes
|
||||||
|
|
||||||
Scenario: Successful login with valid credentials
|
Scenario: Successful login with valid credentials
|
||||||
Given the API is running
|
Given the API is running
|
||||||
@@ -31,4 +31,9 @@ Feature: User Login
|
|||||||
Scenario: Login fails when both username and password are missing
|
Scenario: Login fails when both username and password are missing
|
||||||
Given the API is running
|
Given the API is running
|
||||||
When I submit a login request with both username and password missing
|
When I submit a login request with both username and password missing
|
||||||
Then the response has HTTP status 400
|
Then the response has HTTP status 400
|
||||||
|
|
||||||
|
Scenario: Login endpoint only accepts POST requests
|
||||||
|
Given the API is running
|
||||||
|
When I submit a login request using a GET request
|
||||||
|
Then the response has HTTP status 404
|
||||||
99
src/Core/API/API.Specs/Steps/ApiGeneralSteps.cs
Normal file
99
src/Core/API/API.Specs/Steps/ApiGeneralSteps.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Reqnroll;
|
||||||
|
using FluentAssertions;
|
||||||
|
using API.Specs;
|
||||||
|
|
||||||
|
namespace API.Specs.Steps;
|
||||||
|
|
||||||
|
[Binding]
|
||||||
|
public class ApiGeneralSteps(ScenarioContext scenario)
|
||||||
|
{
|
||||||
|
private const string ClientKey = "client";
|
||||||
|
private const string FactoryKey = "factory";
|
||||||
|
private const string ResponseKey = "response";
|
||||||
|
private const string ResponseBodyKey = "responseBody";
|
||||||
|
|
||||||
|
private HttpClient GetClient()
|
||||||
|
{
|
||||||
|
if (scenario.TryGetValue<HttpClient>(ClientKey, out var client))
|
||||||
|
{
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
var factory = scenario.TryGetValue<TestApiFactory>(FactoryKey, out var f) ? f : new TestApiFactory();
|
||||||
|
scenario[FactoryKey] = factory;
|
||||||
|
|
||||||
|
client = factory.CreateClient();
|
||||||
|
scenario[ClientKey] = client;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Given("the API is running")]
|
||||||
|
public void GivenTheApiIsRunning()
|
||||||
|
{
|
||||||
|
GetClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
[When("I send an HTTP request {string} to {string} with body:")]
|
||||||
|
public async Task WhenISendAnHttpRequestStringToStringWithBody(string method, string url, string jsonBody)
|
||||||
|
{
|
||||||
|
var client = GetClient();
|
||||||
|
|
||||||
|
var requestMessage = new HttpRequestMessage(new HttpMethod(method), url)
|
||||||
|
{
|
||||||
|
Content = new StringContent(jsonBody, System.Text.Encoding.UTF8, "application/json")
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await client.SendAsync(requestMessage);
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
scenario[ResponseKey] = response;
|
||||||
|
scenario[ResponseBodyKey] = responseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
[When("I send an HTTP request {string} to {string}")]
|
||||||
|
public async Task WhenISendAnHttpRequestStringToString(string method, string url)
|
||||||
|
{
|
||||||
|
var client = GetClient();
|
||||||
|
var requestMessage = new HttpRequestMessage(new HttpMethod(method), url);
|
||||||
|
var response = await client.SendAsync(requestMessage);
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
scenario[ResponseKey] = response;
|
||||||
|
scenario[ResponseBodyKey] = responseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Then("the response status code should be {int}")]
|
||||||
|
public void ThenTheResponseStatusCodeShouldBeInt(int expected)
|
||||||
|
{
|
||||||
|
scenario.TryGetValue<HttpResponseMessage>(ResponseKey, out var response).Should().BeTrue();
|
||||||
|
((int)response!.StatusCode).Should().Be(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Then("the response has HTTP status {int}")]
|
||||||
|
public void ThenTheResponseHasHttpStatusInt(int expectedCode)
|
||||||
|
{
|
||||||
|
scenario.TryGetValue<HttpResponseMessage>(ResponseKey, out var response).Should().BeTrue("No response was received from the API");
|
||||||
|
((int)response!.StatusCode).Should().Be(expectedCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Then("the response JSON should have {string} equal {string}")]
|
||||||
|
public void ThenTheResponseJsonShouldHaveStringEqualString(string field, string expected)
|
||||||
|
{
|
||||||
|
scenario.TryGetValue<HttpResponseMessage>(ResponseKey, out var response).Should().BeTrue();
|
||||||
|
scenario.TryGetValue<string>(ResponseBodyKey, out var responseBody).Should().BeTrue();
|
||||||
|
|
||||||
|
using var doc = JsonDocument.Parse(responseBody!);
|
||||||
|
var root = doc.RootElement;
|
||||||
|
|
||||||
|
if (!root.TryGetProperty(field, out var value))
|
||||||
|
{
|
||||||
|
root.TryGetProperty("payload", out var payloadElem).Should().BeTrue("Expected field '{0}' to be present either at the root or inside 'payload'", field);
|
||||||
|
payloadElem.ValueKind.Should().Be(JsonValueKind.Object, "payload must be an object");
|
||||||
|
payloadElem.TryGetProperty(field, out value).Should().BeTrue("Expected field '{0}' to be present inside 'payload'", field);
|
||||||
|
}
|
||||||
|
|
||||||
|
value.ValueKind.Should().Be(JsonValueKind.String, "Expected field '{0}' to be a string", field);
|
||||||
|
value.GetString().Should().Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
using System.Net;
|
|
||||||
using System.Net.Http.Json;
|
|
||||||
using System.Text.Json;
|
|
||||||
using Reqnroll;
|
|
||||||
using FluentAssertions;
|
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
|
||||||
|
|
||||||
namespace API.Specs.Steps;
|
|
||||||
|
|
||||||
[Binding]
|
|
||||||
public class ApiSteps
|
|
||||||
{
|
|
||||||
private readonly TestApiFactory _factory = new();
|
|
||||||
private HttpClient? _client;
|
|
||||||
|
|
||||||
private HttpResponseMessage? _response;
|
|
||||||
private string? _responseBody;
|
|
||||||
|
|
||||||
private (string username, string password) _testUser;
|
|
||||||
|
|
||||||
[Given("the API is running")]
|
|
||||||
public void GivenTheApiIsRunning()
|
|
||||||
{
|
|
||||||
_client = _factory.CreateClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Then("the response status code should be {int}")]
|
|
||||||
public void ThenStatusCodeShouldBe(int expected)
|
|
||||||
{
|
|
||||||
_response.Should().NotBeNull();
|
|
||||||
((int)_response!.StatusCode).Should().Be(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Then("the response JSON should have {string} equal {string}")]
|
|
||||||
public void ThenTheResponseJsonShouldHaveStringEqualString(string field, string expected)
|
|
||||||
{
|
|
||||||
_response.Should().NotBeNull();
|
|
||||||
_responseBody.Should().NotBeNull();
|
|
||||||
|
|
||||||
using var doc = JsonDocument.Parse(_responseBody!);
|
|
||||||
var root = doc.RootElement;
|
|
||||||
|
|
||||||
if (!root.TryGetProperty(field, out var value))
|
|
||||||
{
|
|
||||||
root.TryGetProperty("payload", out var payloadElem).Should().BeTrue("Expected field '{0}' to be present either at the root or inside 'payload'", field);
|
|
||||||
payloadElem.ValueKind.Should().Be(JsonValueKind.Object, "payload must be an object");
|
|
||||||
payloadElem.TryGetProperty(field, out value).Should().BeTrue("Expected field '{0}' to be present inside 'payload'", field);
|
|
||||||
}
|
|
||||||
|
|
||||||
value.ValueKind.Should().Be(JsonValueKind.String, "Expected field '{0}' to be a string", field);
|
|
||||||
value.GetString().Should().Be(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
[When("I send an HTTP request {string} to {string} with body:")]
|
|
||||||
public async Task WhenISendAnHttpRequestStringToStringWithBody(string method, string url, string jsonBody)
|
|
||||||
{
|
|
||||||
_client.Should().NotBeNull();
|
|
||||||
|
|
||||||
var requestMessage = new HttpRequestMessage(new HttpMethod(method), url)
|
|
||||||
{
|
|
||||||
// Convert the string body into JSON content
|
|
||||||
Content = new StringContent(jsonBody, System.Text.Encoding.UTF8, "application/json")
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
_response = await _client!.SendAsync(requestMessage);
|
|
||||||
|
|
||||||
_responseBody = await _response.Content.ReadAsStringAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[When("I send an HTTP request {string} to {string}")]
|
|
||||||
public async Task WhenISendAnHttpRequestTo(string method, string url)
|
|
||||||
{
|
|
||||||
var requestMessage = new HttpRequestMessage(new HttpMethod(method), url);
|
|
||||||
_response = await _client!.SendAsync(requestMessage);
|
|
||||||
_responseBody = await _response.Content.ReadAsStringAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Then("the response has HTTP status {int}")]
|
|
||||||
public void ThenTheResponseHasHttpStatusInt(int expectedCode)
|
|
||||||
{
|
|
||||||
_response.Should().NotBeNull("No response was received from the API");
|
|
||||||
|
|
||||||
((int)_response!.StatusCode).Should().Be(expectedCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Given("I have an existing account")]
|
|
||||||
public void GivenIHaveAnExistingAccount()
|
|
||||||
{
|
|
||||||
_testUser = ("test.user", "password");
|
|
||||||
}
|
|
||||||
|
|
||||||
[When("I submit a login request with a username and password")]
|
|
||||||
public async Task WhenISubmitALoginRequestWithAUsernameAndPassword()
|
|
||||||
{
|
|
||||||
await WhenISendAnHttpRequestStringToStringWithBody("POST", "/api/auth/login", $@"
|
|
||||||
{{
|
|
||||||
""username"": ""{_testUser.username}"",
|
|
||||||
""password"": ""{_testUser.password}""
|
|
||||||
}}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Then("the response JSON should have an access token")]
|
|
||||||
public void ThenTheResponseJsonShouldHaveAnAccessToken()
|
|
||||||
{
|
|
||||||
_response.Should().NotBeNull();
|
|
||||||
_responseBody.Should().NotBeNull();
|
|
||||||
|
|
||||||
using var doc = JsonDocument.Parse(_responseBody!);
|
|
||||||
var root = doc.RootElement;
|
|
||||||
JsonElement tokenElem;
|
|
||||||
var hasToken = root.TryGetProperty("accessToken", out tokenElem)
|
|
||||||
|| root.TryGetProperty("AccessToken", out tokenElem);
|
|
||||||
|
|
||||||
if (!hasToken)
|
|
||||||
{
|
|
||||||
if (root.TryGetProperty("payload", out var payloadElem) && payloadElem.ValueKind == JsonValueKind.Object)
|
|
||||||
{
|
|
||||||
hasToken = payloadElem.TryGetProperty("accessToken", out tokenElem)
|
|
||||||
|| payloadElem.TryGetProperty("AccessToken", out tokenElem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hasToken.Should().BeTrue("Expected an access token either at the root or inside 'payload'");
|
|
||||||
|
|
||||||
var token = tokenElem.GetString();
|
|
||||||
|
|
||||||
// @todo validate the token
|
|
||||||
|
|
||||||
token.Should().NotBeNullOrEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Given("I do not have an existing account")]
|
|
||||||
public void GivenIDoNotHaveAnExistingAccount()
|
|
||||||
{
|
|
||||||
_testUser = ("Failing", "User");
|
|
||||||
}
|
|
||||||
|
|
||||||
[When("I submit a login request with a missing username")]
|
|
||||||
public async Task WhenISubmitALoginRequestWithAMissingUsername()
|
|
||||||
{
|
|
||||||
await WhenISendAnHttpRequestStringToStringWithBody("POST", "/api/auth/login", $@"
|
|
||||||
{{
|
|
||||||
""password"": ""test""
|
|
||||||
}}");
|
|
||||||
}
|
|
||||||
|
|
||||||
[When("I submit a login request with a missing password")]
|
|
||||||
public async Task WhenISubmitALoginRequestWithAMissingPassword()
|
|
||||||
{
|
|
||||||
await WhenISendAnHttpRequestStringToStringWithBody("POST", "/api/auth/login", $@"
|
|
||||||
{{
|
|
||||||
""username"": ""test""
|
|
||||||
}}");
|
|
||||||
}
|
|
||||||
|
|
||||||
[When("I submit a login request with both username and password missing")]
|
|
||||||
public async Task WhenISubmitALoginRequestWithBothUsernameAndPasswordMissing()
|
|
||||||
{
|
|
||||||
await WhenISendAnHttpRequestStringToStringWithBody("POST", "/api/auth/login", "{}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
160
src/Core/API/API.Specs/Steps/AuthSteps.cs
Normal file
160
src/Core/API/API.Specs/Steps/AuthSteps.cs
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Reqnroll;
|
||||||
|
using FluentAssertions;
|
||||||
|
using API.Specs;
|
||||||
|
|
||||||
|
namespace API.Specs.Steps;
|
||||||
|
|
||||||
|
[Binding]
|
||||||
|
public class AuthSteps(ScenarioContext scenario)
|
||||||
|
{
|
||||||
|
private const string ClientKey = "client";
|
||||||
|
private const string FactoryKey = "factory";
|
||||||
|
private const string ResponseKey = "response";
|
||||||
|
private const string ResponseBodyKey = "responseBody";
|
||||||
|
private const string TestUserKey = "testUser";
|
||||||
|
|
||||||
|
private HttpClient GetClient()
|
||||||
|
{
|
||||||
|
if (scenario.TryGetValue<HttpClient>(ClientKey, out var client))
|
||||||
|
{
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
var factory = scenario.TryGetValue<TestApiFactory>(FactoryKey, out var f) ? f : new TestApiFactory();
|
||||||
|
scenario[FactoryKey] = factory;
|
||||||
|
|
||||||
|
client = factory.CreateClient();
|
||||||
|
scenario[ClientKey] = client;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Given("I have an existing account")]
|
||||||
|
public void GivenIHaveAnExistingAccount()
|
||||||
|
{
|
||||||
|
scenario[TestUserKey] = ("test.user", "password");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Given("I do not have an existing account")]
|
||||||
|
public void GivenIDoNotHaveAnExistingAccount()
|
||||||
|
{
|
||||||
|
scenario[TestUserKey] = ("Failing", "User");
|
||||||
|
}
|
||||||
|
|
||||||
|
[When("I submit a login request with a username and password")]
|
||||||
|
public async Task WhenISubmitALoginRequestWithAUsernameAndPassword()
|
||||||
|
{
|
||||||
|
var client = GetClient();
|
||||||
|
var (username, password) = scenario.TryGetValue<(string username, string password)>(TestUserKey, out var user)
|
||||||
|
? user
|
||||||
|
: ("test.user", "password");
|
||||||
|
|
||||||
|
var body = JsonSerializer.Serialize(new { username, password });
|
||||||
|
|
||||||
|
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/api/auth/login")
|
||||||
|
{
|
||||||
|
Content = new StringContent(body, System.Text.Encoding.UTF8, "application/json")
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await client.SendAsync(requestMessage);
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
scenario[ResponseKey] = response;
|
||||||
|
scenario[ResponseBodyKey] = responseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
[When("I submit a login request with a missing username")]
|
||||||
|
public async Task WhenISubmitALoginRequestWithAMissingUsername()
|
||||||
|
{
|
||||||
|
var client = GetClient();
|
||||||
|
var body = JsonSerializer.Serialize(new { password = "test" });
|
||||||
|
|
||||||
|
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/api/auth/login")
|
||||||
|
{
|
||||||
|
Content = new StringContent(body, System.Text.Encoding.UTF8, "application/json")
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await client.SendAsync(requestMessage);
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
scenario[ResponseKey] = response;
|
||||||
|
scenario[ResponseBodyKey] = responseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
[When("I submit a login request with a missing password")]
|
||||||
|
public async Task WhenISubmitALoginRequestWithAMissingPassword()
|
||||||
|
{
|
||||||
|
var client = GetClient();
|
||||||
|
var body = JsonSerializer.Serialize(new { username = "test" });
|
||||||
|
|
||||||
|
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/api/auth/login")
|
||||||
|
{
|
||||||
|
Content = new StringContent(body, System.Text.Encoding.UTF8, "application/json")
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await client.SendAsync(requestMessage);
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
scenario[ResponseKey] = response;
|
||||||
|
scenario[ResponseBodyKey] = responseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
[When("I submit a login request with both username and password missing")]
|
||||||
|
public async Task WhenISubmitALoginRequestWithBothUsernameAndPasswordMissing()
|
||||||
|
{
|
||||||
|
var client = GetClient();
|
||||||
|
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/api/auth/login")
|
||||||
|
{
|
||||||
|
Content = new StringContent("{}", System.Text.Encoding.UTF8, "application/json")
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await client.SendAsync(requestMessage);
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
scenario[ResponseKey] = response;
|
||||||
|
scenario[ResponseBodyKey] = responseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Then("the response JSON should have an access token")]
|
||||||
|
public void ThenTheResponseJsonShouldHaveAnAccessToken()
|
||||||
|
{
|
||||||
|
scenario.TryGetValue<HttpResponseMessage>(ResponseKey, out var response).Should().BeTrue();
|
||||||
|
scenario.TryGetValue<string>(ResponseBodyKey, out var responseBody).Should().BeTrue();
|
||||||
|
|
||||||
|
var doc = JsonDocument.Parse(responseBody!);
|
||||||
|
var root = doc.RootElement;
|
||||||
|
JsonElement tokenElem = default;
|
||||||
|
var hasToken = false;
|
||||||
|
|
||||||
|
|
||||||
|
if (root.TryGetProperty("payload", out var payloadElem) && payloadElem.ValueKind == JsonValueKind.Object)
|
||||||
|
{
|
||||||
|
hasToken = payloadElem.TryGetProperty("accessToken", out tokenElem)
|
||||||
|
|| payloadElem.TryGetProperty("AccessToken", out tokenElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hasToken.Should().BeTrue("Expected an access token either at the root or inside 'payload'");
|
||||||
|
|
||||||
|
var token = tokenElem.GetString();
|
||||||
|
token.Should().NotBeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[When("I submit a login request using a GET request")]
|
||||||
|
public async Task WhenISubmitALoginRequestUsingAgetRequest()
|
||||||
|
{
|
||||||
|
var client = GetClient();
|
||||||
|
// testing GET
|
||||||
|
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "/api/auth/login")
|
||||||
|
{
|
||||||
|
Content = new StringContent("{}", System.Text.Encoding.UTF8, "application/json")
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await client.SendAsync(requestMessage);
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
scenario[ResponseKey] = response;
|
||||||
|
scenario[ResponseBodyKey] = responseBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user