Edit auth bdd tests

This commit is contained in:
Aaron Po
2026-02-01 12:33:24 -05:00
parent 954e224c34
commit ee53cc60d8
4 changed files with 268 additions and 169 deletions

View File

@@ -32,3 +32,8 @@ Feature: User Login
Given the API is running
When I submit a login request with both username and password missing
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

View 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);
}
}

View File

@@ -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", "{}");
}
}

View 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;
}
}