mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-14 17:54:42 +00:00
Enforce brewery ownership on update and delete (#267)
This commit is contained in:
39
web/backend/API/API.Specs/Steps/ApiStepsBase.cs
Normal file
39
web/backend/API/API.Specs/Steps/ApiStepsBase.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Reqnroll;
|
||||
|
||||
namespace API.Specs.Steps;
|
||||
|
||||
/// <summary>
|
||||
/// Shared <see cref="TestApiFactory" />/<see cref="HttpClient" /> plumbing and the <see cref="ScenarioContext" />
|
||||
/// keys used to coordinate it, common to every step definition class that drives the test API.
|
||||
/// </summary>
|
||||
public abstract class ApiStepsBase(ScenarioContext scenario)
|
||||
{
|
||||
protected ScenarioContext Scenario { get; } = scenario;
|
||||
|
||||
protected const string ClientKey = "client";
|
||||
protected const string FactoryKey = "factory";
|
||||
protected const string ResponseKey = "response";
|
||||
protected const string ResponseBodyKey = "responseBody";
|
||||
|
||||
protected TestApiFactory GetFactory()
|
||||
{
|
||||
TestApiFactory? factory = Scenario.TryGetValue<TestApiFactory>(
|
||||
FactoryKey,
|
||||
out TestApiFactory? f
|
||||
)
|
||||
? f
|
||||
: new TestApiFactory();
|
||||
Scenario[FactoryKey] = factory;
|
||||
return factory;
|
||||
}
|
||||
|
||||
protected HttpClient GetClient()
|
||||
{
|
||||
if (Scenario.TryGetValue<HttpClient>(ClientKey, out HttpClient? client))
|
||||
return client;
|
||||
|
||||
client = GetFactory().CreateClient();
|
||||
Scenario[ClientKey] = client;
|
||||
return client;
|
||||
}
|
||||
}
|
||||
@@ -10,40 +10,14 @@ using Reqnroll;
|
||||
namespace API.Specs.Steps;
|
||||
|
||||
[Binding]
|
||||
public class AuthSteps(ScenarioContext scenario)
|
||||
public class AuthSteps(ScenarioContext scenario) : ApiStepsBase(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 const string RegisteredUserIdKey = "registeredUserId";
|
||||
private const string RegisteredUsernameKey = "registeredUsername";
|
||||
private const string PreviousAccessTokenKey = "previousAccessToken";
|
||||
private const string PreviousRefreshTokenKey = "previousRefreshToken";
|
||||
|
||||
private TestApiFactory GetFactory()
|
||||
{
|
||||
TestApiFactory? factory = scenario.TryGetValue<TestApiFactory>(
|
||||
FactoryKey,
|
||||
out TestApiFactory? f
|
||||
)
|
||||
? f
|
||||
: new TestApiFactory();
|
||||
scenario[FactoryKey] = factory;
|
||||
return factory;
|
||||
}
|
||||
|
||||
private HttpClient GetClient()
|
||||
{
|
||||
if (scenario.TryGetValue<HttpClient>(ClientKey, out HttpClient? client))
|
||||
return client;
|
||||
|
||||
client = GetFactory().CreateClient();
|
||||
scenario[ClientKey] = client;
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a required configuration value from the running <see cref="TestApiFactory" />'s own
|
||||
/// <see cref="IConfiguration" />, so forged tokens are always signed with the exact secret the
|
||||
@@ -107,7 +81,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I have an existing account")]
|
||||
public async Task GivenIHaveAnExistingAccount()
|
||||
{
|
||||
scenario[TestUserKey] = (FixtureUsername, FixturePassword);
|
||||
Scenario[TestUserKey] = (FixtureUsername, FixturePassword);
|
||||
|
||||
HttpClient client = GetClient();
|
||||
var registrationData = new
|
||||
@@ -153,14 +127,14 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I do not have an existing account")]
|
||||
public void GivenIDoNotHaveAnExistingAccount()
|
||||
{
|
||||
scenario[TestUserKey] = ("Failing", "User");
|
||||
Scenario[TestUserKey] = ("Failing", "User");
|
||||
}
|
||||
|
||||
[When("I submit a login request with a username and password")]
|
||||
public async Task WhenISubmitALoginRequestWithAUsernameAndPassword()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
(string username, string password) = scenario.TryGetValue(TestUserKey, out (string username, string password) user)
|
||||
(string username, string password) = Scenario.TryGetValue(TestUserKey, out (string username, string password) user)
|
||||
? user
|
||||
: ("test.user", "password");
|
||||
|
||||
@@ -174,8 +148,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a login request with a missing username")]
|
||||
@@ -192,8 +166,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a login request with a missing password")]
|
||||
@@ -210,8 +184,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a login request with both username and password missing")]
|
||||
@@ -226,18 +200,18 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[Then("the response JSON should have an access token")]
|
||||
public void ThenTheResponseJsonShouldHaveAnAccessToken()
|
||||
{
|
||||
scenario
|
||||
Scenario
|
||||
.TryGetValue<HttpResponseMessage>(ResponseKey, out HttpResponseMessage? response)
|
||||
.Should()
|
||||
.BeTrue();
|
||||
scenario.TryGetValue<string>(ResponseBodyKey, out string? responseBody).Should().BeTrue();
|
||||
Scenario.TryGetValue<string>(ResponseBodyKey, out string? responseBody).Should().BeTrue();
|
||||
|
||||
JsonDocument doc = JsonDocument.Parse(responseBody!);
|
||||
JsonElement root = doc.RootElement;
|
||||
@@ -270,8 +244,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a registration request with values:")]
|
||||
@@ -321,8 +295,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a registration request using a GET request")]
|
||||
@@ -337,8 +311,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[Given("I have registered a new account")]
|
||||
@@ -364,13 +338,13 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
|
||||
using JsonDocument doc = JsonDocument.Parse(responseBody);
|
||||
JsonElement root = doc.RootElement;
|
||||
scenario[RegisteredUserIdKey] = ParseRegisteredUserId(root);
|
||||
scenario[RegisteredUsernameKey] = ParseRegisteredUsername(root);
|
||||
Scenario[RegisteredUserIdKey] = ParseRegisteredUserId(root);
|
||||
Scenario[RegisteredUsernameKey] = ParseRegisteredUsername(root);
|
||||
}
|
||||
|
||||
[Given("I am logged in")]
|
||||
@@ -396,12 +370,12 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
payloadElem.TryGetProperty("accessToken", out JsonElement tokenElem)
|
||||
|| payloadElem.TryGetProperty("AccessToken", out tokenElem)
|
||||
)
|
||||
scenario["accessToken"] = tokenElem.GetString();
|
||||
Scenario["accessToken"] = tokenElem.GetString();
|
||||
if (
|
||||
payloadElem.TryGetProperty("refreshToken", out JsonElement refreshElem)
|
||||
|| payloadElem.TryGetProperty("RefreshToken", out refreshElem)
|
||||
)
|
||||
scenario["refreshToken"] = refreshElem.GetString();
|
||||
Scenario["refreshToken"] = refreshElem.GetString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,15 +395,15 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I have a valid access token for my account")]
|
||||
public void GivenIHaveAValidAccessTokenForMyAccount()
|
||||
{
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = Scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("registered user ID not found in scenario");
|
||||
string? username = scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
string? username = Scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
? user
|
||||
: throw new InvalidOperationException("registered username not found in scenario");
|
||||
|
||||
string secret = GetRequiredConfigValue(ConfigurationKeys.AccessTokenSecret);
|
||||
scenario["accessToken"] = GenerateJwtToken(
|
||||
Scenario["accessToken"] = GenerateJwtToken(
|
||||
userId,
|
||||
username,
|
||||
secret,
|
||||
@@ -440,15 +414,15 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I have a valid confirmation token for my account")]
|
||||
public void GivenIHaveAValidConfirmationTokenForMyAccount()
|
||||
{
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = Scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("registered user ID not found in scenario");
|
||||
string? username = scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
string? username = Scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
? user
|
||||
: throw new InvalidOperationException("registered username not found in scenario");
|
||||
|
||||
string secret = GetRequiredConfigValue(ConfigurationKeys.ConfirmationTokenSecret);
|
||||
scenario["confirmationToken"] = GenerateJwtToken(
|
||||
Scenario["confirmationToken"] = GenerateJwtToken(
|
||||
userId,
|
||||
username,
|
||||
secret,
|
||||
@@ -459,15 +433,15 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I have an expired confirmation token for my account")]
|
||||
public void GivenIHaveAnExpiredConfirmationTokenForMyAccount()
|
||||
{
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = Scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("registered user ID not found in scenario");
|
||||
string? username = scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
string? username = Scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
? user
|
||||
: throw new InvalidOperationException("registered username not found in scenario");
|
||||
|
||||
string secret = GetRequiredConfigValue(ConfigurationKeys.ConfirmationTokenSecret);
|
||||
scenario["confirmationToken"] = GenerateJwtToken(
|
||||
Scenario["confirmationToken"] = GenerateJwtToken(
|
||||
userId,
|
||||
username,
|
||||
secret,
|
||||
@@ -478,15 +452,15 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I have a confirmation token signed with the wrong secret")]
|
||||
public void GivenIHaveAConfirmationTokenSignedWithTheWrongSecret()
|
||||
{
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = Scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("registered user ID not found in scenario");
|
||||
string? username = scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
string? username = Scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
? user
|
||||
: throw new InvalidOperationException("registered username not found in scenario");
|
||||
|
||||
const string wrongSecret = "wrong-confirmation-secret-that-is-very-long-1234567890";
|
||||
scenario["confirmationToken"] = GenerateJwtToken(
|
||||
Scenario["confirmationToken"] = GenerateJwtToken(
|
||||
userId,
|
||||
username,
|
||||
wrongSecret,
|
||||
@@ -498,7 +472,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
public async Task WhenISubmitARequestToAProtectedEndpointWithAValidAccessToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("accessToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("accessToken", out string? t)
|
||||
? t
|
||||
: "invalid-token";
|
||||
|
||||
@@ -509,8 +483,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a request to a protected endpoint with an invalid access token")]
|
||||
@@ -524,18 +498,18 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a confirmation request with the valid token")]
|
||||
public async Task WhenISubmitAConfirmationRequestWithTheValidToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
? t
|
||||
: "valid-token";
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
string? accessToken = Scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
? at
|
||||
: string.Empty;
|
||||
|
||||
@@ -548,18 +522,18 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit the same confirmation request again")]
|
||||
public async Task WhenISubmitTheSameConfirmationRequestAgain()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
? t
|
||||
: "valid-token";
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
string? accessToken = Scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
? at
|
||||
: string.Empty;
|
||||
|
||||
@@ -572,8 +546,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a confirmation request with a malformed token")]
|
||||
@@ -581,7 +555,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
const string token = "malformed-token-not-jwt";
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
string? accessToken = Scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
? at
|
||||
: string.Empty;
|
||||
|
||||
@@ -594,20 +568,20 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a refresh token request with a valid refresh token")]
|
||||
public async Task WhenISubmitARefreshTokenRequestWithTheValidRefreshToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
if (scenario.TryGetValue<string>("accessToken", out string? oldAccessToken))
|
||||
scenario[PreviousAccessTokenKey] = oldAccessToken;
|
||||
if (scenario.TryGetValue<string>("refreshToken", out string? oldRefreshToken))
|
||||
scenario[PreviousRefreshTokenKey] = oldRefreshToken;
|
||||
if (Scenario.TryGetValue<string>("accessToken", out string? oldAccessToken))
|
||||
Scenario[PreviousAccessTokenKey] = oldAccessToken;
|
||||
if (Scenario.TryGetValue<string>("refreshToken", out string? oldRefreshToken))
|
||||
Scenario[PreviousRefreshTokenKey] = oldRefreshToken;
|
||||
|
||||
string? token = scenario.TryGetValue<string>("refreshToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("refreshToken", out string? t)
|
||||
? t
|
||||
: "valid-refresh-token";
|
||||
|
||||
@@ -616,8 +590,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a refresh token request with an invalid refresh token")]
|
||||
@@ -630,8 +604,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a refresh token request with the expired refresh token")]
|
||||
@@ -644,8 +618,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a refresh token request with a missing refresh token")]
|
||||
@@ -657,8 +631,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a refresh token request using a GET request")]
|
||||
@@ -672,8 +646,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
// Protected Endpoint Steps
|
||||
@@ -685,22 +659,22 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[Given("I am logged in with an immediately-expiring access token")]
|
||||
public Task GivenIAmLoggedInWithAnImmediatelyExpiringAccessToken()
|
||||
{
|
||||
// Simulate an expired access token for auth rejection behavior.
|
||||
scenario["accessToken"] = "expired-access-token";
|
||||
Scenario["accessToken"] = "expired-access-token";
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Given("I have an access token signed with the wrong secret")]
|
||||
public void GivenIHaveAnAccessTokenSignedWithTheWrongSecret()
|
||||
{
|
||||
scenario["accessToken"] =
|
||||
Scenario["accessToken"] =
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
||||
}
|
||||
|
||||
@@ -708,7 +682,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
public async Task WhenISubmitARequestToAProtectedEndpointWithTheExpiredToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("accessToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("accessToken", out string? t)
|
||||
? t
|
||||
: "expired-token";
|
||||
|
||||
@@ -719,15 +693,15 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a request to a protected endpoint with the tampered token")]
|
||||
public async Task WhenISubmitARequestToAProtectedEndpointWithTheTamperedToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("accessToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("accessToken", out string? t)
|
||||
? t
|
||||
: "tampered-token";
|
||||
|
||||
@@ -738,8 +712,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When(
|
||||
@@ -748,7 +722,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
public async Task WhenISubmitARequestToAProtectedEndpointWithMyRefreshTokenInsteadOfAccessToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("refreshToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("refreshToken", out string? t)
|
||||
? t
|
||||
: "refresh-token";
|
||||
|
||||
@@ -759,24 +733,24 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[Given("I have a valid confirmation token")]
|
||||
public void GivenIHaveAValidConfirmationToken()
|
||||
{
|
||||
scenario["confirmationToken"] = "valid-confirmation-token";
|
||||
Scenario["confirmationToken"] = "valid-confirmation-token";
|
||||
}
|
||||
|
||||
[When("I submit a confirmation request with the expired token")]
|
||||
public async Task WhenISubmitAConfirmationRequestWithTheExpiredToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
? t
|
||||
: "expired-confirmation-token";
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
string? accessToken = Scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
? at
|
||||
: string.Empty;
|
||||
|
||||
@@ -789,18 +763,18 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a confirmation request with the tampered token")]
|
||||
public async Task WhenISubmitAConfirmationRequestWithTheTamperedToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
? t
|
||||
: "tampered-confirmation-token";
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
string? accessToken = Scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
? at
|
||||
: string.Empty;
|
||||
|
||||
@@ -813,15 +787,15 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a confirmation request with a missing token")]
|
||||
public async Task WhenISubmitAConfirmationRequestWithAMissingToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
string? accessToken = Scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
? at
|
||||
: string.Empty;
|
||||
|
||||
@@ -831,15 +805,15 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a confirmation request using an invalid HTTP method")]
|
||||
public async Task WhenISubmitAConfirmationRequestUsingAnInvalidHttpMethod()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
? t
|
||||
: "valid-confirmation-token";
|
||||
|
||||
@@ -850,8 +824,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When(
|
||||
@@ -860,7 +834,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
public async Task WhenISubmitARequestToAProtectedEndpointWithMyConfirmationTokenInsteadOfAccessToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
? t
|
||||
: "confirmation-token";
|
||||
|
||||
@@ -871,8 +845,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a confirmation request with an invalid token")]
|
||||
@@ -880,7 +854,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
const string token = "invalid-confirmation-token";
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
string? accessToken = Scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
? at
|
||||
: string.Empty;
|
||||
|
||||
@@ -893,15 +867,15 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a confirmation request with the valid token without an access token")]
|
||||
public async Task WhenISubmitAConfirmationRequestWithTheValidTokenWithoutAnAccessToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
? t
|
||||
: "valid-token";
|
||||
|
||||
@@ -912,14 +886,14 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[Then("the response JSON should have a new access token")]
|
||||
public void ThenTheResponseJsonShouldHaveANewAccessToken()
|
||||
{
|
||||
scenario.TryGetValue<string>(ResponseBodyKey, out string? responseBody).Should().BeTrue();
|
||||
Scenario.TryGetValue<string>(ResponseBodyKey, out string? responseBody).Should().BeTrue();
|
||||
|
||||
using JsonDocument doc = JsonDocument.Parse(responseBody!);
|
||||
JsonElement payload = doc.RootElement.GetProperty("payload");
|
||||
@@ -927,14 +901,14 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
accessToken.Should().NotBeNullOrWhiteSpace();
|
||||
|
||||
if (scenario.TryGetValue<string>(PreviousAccessTokenKey, out string? previousAccessToken))
|
||||
if (Scenario.TryGetValue<string>(PreviousAccessTokenKey, out string? previousAccessToken))
|
||||
accessToken.Should().NotBe(previousAccessToken);
|
||||
}
|
||||
|
||||
[Then("the response JSON should have a new refresh token")]
|
||||
public void ThenTheResponseJsonShouldHaveANewRefreshToken()
|
||||
{
|
||||
scenario.TryGetValue<string>(ResponseBodyKey, out string? responseBody).Should().BeTrue();
|
||||
Scenario.TryGetValue<string>(ResponseBodyKey, out string? responseBody).Should().BeTrue();
|
||||
|
||||
using JsonDocument doc = JsonDocument.Parse(responseBody!);
|
||||
JsonElement payload = doc.RootElement.GetProperty("payload");
|
||||
@@ -942,7 +916,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
refreshToken.Should().NotBeNullOrWhiteSpace();
|
||||
|
||||
if (scenario.TryGetValue<string>(PreviousRefreshTokenKey, out string? previousRefreshToken))
|
||||
if (Scenario.TryGetValue<string>(PreviousRefreshTokenKey, out string? previousRefreshToken))
|
||||
refreshToken.Should().NotBe(previousRefreshToken);
|
||||
}
|
||||
|
||||
@@ -950,10 +924,10 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
public async Task GivenIHaveConfirmedMyAccount()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string? token = scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
string? token = Scenario.TryGetValue<string>("confirmationToken", out string? t)
|
||||
? t
|
||||
: throw new InvalidOperationException("confirmation token not found");
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
string? accessToken = Scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
? at
|
||||
: string.Empty;
|
||||
|
||||
@@ -972,10 +946,10 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
public async Task WhenISubmitAResendConfirmationRequestForMyAccount()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = Scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("registered user ID not found");
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
string? accessToken = Scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
? at
|
||||
: string.Empty;
|
||||
|
||||
@@ -988,8 +962,8 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a resend confirmation request for a non-existent user")]
|
||||
@@ -997,7 +971,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
Guid fakeUserId = Guid.NewGuid();
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
string? accessToken = Scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
? at
|
||||
: string.Empty;
|
||||
|
||||
@@ -1010,15 +984,15 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[When("I submit a resend confirmation request without an access token")]
|
||||
public async Task WhenISubmitAResendConfirmationRequestWithoutAnAccessToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id) ? id : Guid.NewGuid();
|
||||
Guid userId = Scenario.TryGetValue(RegisteredUserIdKey, out Guid id) ? id : Guid.NewGuid();
|
||||
|
||||
HttpRequestMessage requestMessage = new(
|
||||
HttpMethod.Post,
|
||||
@@ -1027,7 +1001,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
scenario[ResponseKey] = response;
|
||||
scenario[ResponseBodyKey] = responseBody;
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
}
|
||||
|
||||
329
web/backend/API/API.Specs/Steps/BrewerySteps.cs
Normal file
329
web/backend/API/API.Specs/Steps/BrewerySteps.cs
Normal file
@@ -0,0 +1,329 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Database.Connection;
|
||||
using Features.Locations.Dtos;
|
||||
using Features.Locations.Repository;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Reqnroll;
|
||||
|
||||
namespace API.Specs.Steps;
|
||||
|
||||
[Binding]
|
||||
public class BrewerySteps(ScenarioContext scenario) : ApiStepsBase(scenario)
|
||||
{
|
||||
private const string AccessTokenKey = "accessToken";
|
||||
private const string CityIdKey = "cityId";
|
||||
private const string BreweryPostIdKey = "breweryPostId";
|
||||
private const string BreweryRowVersionKey = "breweryRowVersion";
|
||||
|
||||
private const string FixtureCountryName = "Fixture Country";
|
||||
private const string FixtureCountryIsoCode = "FX";
|
||||
private const string FixtureStateProvinceName = "Fixture State";
|
||||
private const string FixtureStateProvinceIsoCode = "FX-TS";
|
||||
private const string FixtureCityName = "Fixture City";
|
||||
|
||||
// GetOrCreateCityIdAsync is not fully race-safe under concurrent callers creating the same
|
||||
// city (see its remarks); scenarios run in parallel across feature classes, so provisioning
|
||||
// the shared fixture city is serialized the same way AuthSteps serializes its fixture account.
|
||||
private static readonly SemaphoreSlim CityProvisioningLock = new(1, 1);
|
||||
|
||||
private HttpRequestMessage NewAuthenticatedRequest(HttpMethod method, string url)
|
||||
{
|
||||
HttpRequestMessage requestMessage = new(method, url);
|
||||
if (Scenario.TryGetValue<string>(AccessTokenKey, out string? accessToken))
|
||||
requestMessage.Headers.Add("Authorization", $"Bearer {accessToken}");
|
||||
return requestMessage;
|
||||
}
|
||||
|
||||
private async Task SendAsync(HttpRequestMessage requestMessage)
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Scenario[ResponseKey] = response;
|
||||
Scenario[ResponseBodyKey] = responseBody;
|
||||
}
|
||||
|
||||
[Given("a city exists")]
|
||||
public async Task GivenACityExists()
|
||||
{
|
||||
ISqlConnectionFactory connectionFactory = GetFactory()
|
||||
.Services.GetRequiredService<ISqlConnectionFactory>();
|
||||
LocationRepository repository = new(connectionFactory);
|
||||
|
||||
CityLocation fixtureCity = new(
|
||||
FixtureCityName,
|
||||
FixtureStateProvinceName,
|
||||
FixtureStateProvinceIsoCode,
|
||||
FixtureCountryName,
|
||||
FixtureCountryIsoCode
|
||||
);
|
||||
|
||||
await CityProvisioningLock.WaitAsync();
|
||||
Guid cityId;
|
||||
try
|
||||
{
|
||||
cityId = await repository.GetOrCreateCityIdAsync(fixtureCity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CityProvisioningLock.Release();
|
||||
}
|
||||
|
||||
Scenario[CityIdKey] = cityId;
|
||||
}
|
||||
|
||||
private async Task CreateBreweryAsync(
|
||||
string breweryName,
|
||||
string description,
|
||||
string addressLine1,
|
||||
string postalCode,
|
||||
Guid cityId
|
||||
)
|
||||
{
|
||||
var createData = new
|
||||
{
|
||||
breweryName,
|
||||
description,
|
||||
location = new
|
||||
{
|
||||
cityId,
|
||||
addressLine1,
|
||||
addressLine2 = (string?)null,
|
||||
postalCode,
|
||||
coordinates = (object?)null,
|
||||
},
|
||||
};
|
||||
|
||||
HttpRequestMessage requestMessage = NewAuthenticatedRequest(
|
||||
HttpMethod.Post,
|
||||
"/api/brewery"
|
||||
);
|
||||
requestMessage.Content = new StringContent(
|
||||
JsonSerializer.Serialize(createData),
|
||||
Encoding.UTF8,
|
||||
"application/json"
|
||||
);
|
||||
|
||||
await SendAsync(requestMessage);
|
||||
}
|
||||
|
||||
[When("I create a brewery with values:")]
|
||||
public async Task WhenICreateABreweryWithValues(Table table)
|
||||
{
|
||||
DataTableRow row = table.Rows[0];
|
||||
Guid cityId = Scenario.TryGetValue(CityIdKey, out Guid id) ? id : Guid.NewGuid();
|
||||
|
||||
await CreateBreweryAsync(
|
||||
row["BreweryName"] ?? "",
|
||||
row["Description"] ?? "",
|
||||
row["AddressLine1"] ?? "",
|
||||
row["PostalCode"] ?? "",
|
||||
cityId
|
||||
);
|
||||
}
|
||||
|
||||
[When("I create a brewery with a non-existent city")]
|
||||
public async Task WhenICreateABreweryWithANonExistentCity()
|
||||
{
|
||||
await CreateBreweryAsync(
|
||||
"Ghost City Brewery",
|
||||
"A brewery in a city that does not exist",
|
||||
"1 Nowhere St",
|
||||
"00000",
|
||||
Guid.NewGuid()
|
||||
);
|
||||
}
|
||||
|
||||
[Given("I have created a brewery")]
|
||||
public async Task GivenIHaveCreatedABrewery()
|
||||
{
|
||||
Guid cityId = Scenario.TryGetValue(CityIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("city ID not found in scenario");
|
||||
|
||||
await CreateBreweryAsync(
|
||||
"Test Brewery",
|
||||
"A test brewery",
|
||||
"123 Test St",
|
||||
"00000",
|
||||
cityId
|
||||
);
|
||||
|
||||
Scenario
|
||||
.TryGetValue<HttpResponseMessage>(ResponseKey, out HttpResponseMessage? response)
|
||||
.Should()
|
||||
.BeTrue();
|
||||
((int)response!.StatusCode).Should().Be(201, "fixture brewery creation must succeed");
|
||||
|
||||
Scenario.TryGetValue<string>(ResponseBodyKey, out string? responseBody).Should().BeTrue();
|
||||
using JsonDocument doc = JsonDocument.Parse(responseBody!);
|
||||
JsonElement payload = doc.RootElement.GetProperty("payload");
|
||||
|
||||
Scenario[BreweryPostIdKey] = payload.GetProperty("breweryPostId").GetGuid();
|
||||
Scenario[BreweryRowVersionKey] = payload.GetProperty("rowVersion").GetString()
|
||||
?? throw new InvalidOperationException("rowVersion missing from created brewery");
|
||||
}
|
||||
|
||||
private Guid GetCreatedBreweryPostId()
|
||||
{
|
||||
return Scenario.TryGetValue(BreweryPostIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("brewery post ID not found in scenario");
|
||||
}
|
||||
|
||||
[When("I retrieve the brewery by ID")]
|
||||
public async Task WhenIRetrieveTheBreweryById()
|
||||
{
|
||||
HttpRequestMessage requestMessage = NewAuthenticatedRequest(
|
||||
HttpMethod.Get,
|
||||
$"/api/brewery/{GetCreatedBreweryPostId()}"
|
||||
);
|
||||
await SendAsync(requestMessage);
|
||||
}
|
||||
|
||||
[When("I retrieve a brewery by a non-existent ID")]
|
||||
public async Task WhenIRetrieveABreweryByANonExistentId()
|
||||
{
|
||||
HttpRequestMessage requestMessage = NewAuthenticatedRequest(
|
||||
HttpMethod.Get,
|
||||
$"/api/brewery/{Guid.NewGuid()}"
|
||||
);
|
||||
await SendAsync(requestMessage);
|
||||
}
|
||||
|
||||
[When("I update the brewery with values:")]
|
||||
public async Task WhenIUpdateTheBreweryWithValues(Table table)
|
||||
{
|
||||
DataTableRow row = table.Rows[0];
|
||||
Guid breweryPostId = GetCreatedBreweryPostId();
|
||||
string rowVersion = Scenario.TryGetValue<string>(BreweryRowVersionKey, out string? rv)
|
||||
? rv
|
||||
: throw new InvalidOperationException("row version not found in scenario");
|
||||
|
||||
var updateData = new
|
||||
{
|
||||
breweryPostId,
|
||||
rowVersion,
|
||||
breweryName = row["BreweryName"] ?? "",
|
||||
description = row["Description"] ?? "",
|
||||
location = (object?)null,
|
||||
};
|
||||
|
||||
HttpRequestMessage requestMessage = NewAuthenticatedRequest(
|
||||
HttpMethod.Put,
|
||||
$"/api/brewery/{breweryPostId}"
|
||||
);
|
||||
requestMessage.Content = new StringContent(
|
||||
JsonSerializer.Serialize(updateData),
|
||||
Encoding.UTF8,
|
||||
"application/json"
|
||||
);
|
||||
|
||||
await SendAsync(requestMessage);
|
||||
}
|
||||
|
||||
[When("I update a non-existent brewery")]
|
||||
public async Task WhenIUpdateANonExistentBrewery()
|
||||
{
|
||||
Guid breweryPostId = Guid.NewGuid();
|
||||
var updateData = new
|
||||
{
|
||||
breweryPostId,
|
||||
rowVersion = Convert.ToBase64String([0x00, 0x00, 0x00, 0x00]),
|
||||
breweryName = "Nonexistent",
|
||||
description = "Nonexistent",
|
||||
location = (object?)null,
|
||||
};
|
||||
|
||||
HttpRequestMessage requestMessage = NewAuthenticatedRequest(
|
||||
HttpMethod.Put,
|
||||
$"/api/brewery/{breweryPostId}"
|
||||
);
|
||||
requestMessage.Content = new StringContent(
|
||||
JsonSerializer.Serialize(updateData),
|
||||
Encoding.UTF8,
|
||||
"application/json"
|
||||
);
|
||||
|
||||
await SendAsync(requestMessage);
|
||||
}
|
||||
|
||||
[When("I delete the brewery")]
|
||||
public async Task WhenIDeleteTheBrewery()
|
||||
{
|
||||
HttpRequestMessage requestMessage = NewAuthenticatedRequest(
|
||||
HttpMethod.Delete,
|
||||
$"/api/brewery/{GetCreatedBreweryPostId()}"
|
||||
);
|
||||
await SendAsync(requestMessage);
|
||||
}
|
||||
|
||||
[When("I delete a non-existent brewery")]
|
||||
public async Task WhenIDeleteANonExistentBrewery()
|
||||
{
|
||||
HttpRequestMessage requestMessage = NewAuthenticatedRequest(
|
||||
HttpMethod.Delete,
|
||||
$"/api/brewery/{Guid.NewGuid()}"
|
||||
);
|
||||
await SendAsync(requestMessage);
|
||||
}
|
||||
|
||||
[Given("I am logged in as a different user")]
|
||||
public async Task GivenIAmLoggedInAsADifferentUser()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
string suffix = Guid.NewGuid().ToString("N")[..8];
|
||||
var registrationData = new
|
||||
{
|
||||
username = $"otheruser-{suffix}",
|
||||
firstName = "Other",
|
||||
lastName = "User",
|
||||
email = $"otheruser-{suffix}@example.com",
|
||||
dateOfBirth = "1990-01-01",
|
||||
password = "Password1!",
|
||||
};
|
||||
|
||||
HttpRequestMessage requestMessage = new(HttpMethod.Post, "/api/auth/register")
|
||||
{
|
||||
Content = new StringContent(
|
||||
JsonSerializer.Serialize(registrationData),
|
||||
Encoding.UTF8,
|
||||
"application/json"
|
||||
),
|
||||
};
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
using JsonDocument doc = JsonDocument.Parse(responseBody);
|
||||
JsonElement payload = doc.RootElement.GetProperty("payload");
|
||||
string accessToken =
|
||||
(
|
||||
payload.TryGetProperty("accessToken", out JsonElement tokenElem)
|
||||
? tokenElem.GetString()
|
||||
: null
|
||||
) ?? throw new InvalidOperationException("accessToken missing from registration payload");
|
||||
|
||||
Scenario[AccessTokenKey] = accessToken;
|
||||
}
|
||||
|
||||
[Then("retrieving the brewery by ID should now return HTTP status {int}")]
|
||||
public async Task ThenRetrievingTheBreweryByIdShouldNowReturnHttpStatus(int expectedCode)
|
||||
{
|
||||
HttpRequestMessage requestMessage = NewAuthenticatedRequest(
|
||||
HttpMethod.Get,
|
||||
$"/api/brewery/{GetCreatedBreweryPostId()}"
|
||||
);
|
||||
await SendAsync(requestMessage);
|
||||
|
||||
Scenario
|
||||
.TryGetValue<HttpResponseMessage>(ResponseKey, out HttpResponseMessage? response)
|
||||
.Should()
|
||||
.BeTrue();
|
||||
((int)response!.StatusCode).Should().Be(expectedCode);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user