Skip to content

Commit

Permalink
Add single integration test to demonstrate token refreshing
Browse files Browse the repository at this point in the history
  • Loading branch information
myieye committed Nov 6, 2024
1 parent 01228cb commit d1d36ff
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 14 deletions.
7 changes: 5 additions & 2 deletions backend/Testing/ApiTests/ApiTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,12 @@ public void ClearCookies()
JwtHelper.ClearCookies(_httpClientHandler);
}

public async Task<JsonObject> ExecuteGql([StringSyntax("graphql")] string gql, bool expectGqlError = false, bool expectSuccessCode = true)
public async Task<JsonObject> ExecuteGql([StringSyntax("graphql")] string gql, bool expectGqlError = false, bool expectSuccessCode = true,
string? overrideJwt = null)
{
var response = await HttpClient.PostAsJsonAsync($"{BaseUrl}/api/graphql", new { query = gql });
var jwtParam = overrideJwt is not null ? $"?jwt={overrideJwt}" : "";
var response = await HttpClient.PostAsJsonAsync($"{BaseUrl}/api/graphql{jwtParam}", new { query = gql });
if (JwtHelper.TryGetJwtFromLoginResponse(response, out var jwt)) CurrJwt = jwt;
var jsonResponse = await response.Content.ReadFromJsonAsync<JsonObject>();
jsonResponse.ShouldNotBeNull($"for query {gql} ({(int)response.StatusCode} ({response.ReasonPhrase}))");
GqlUtils.ValidateGqlErrors(jsonResponse, expectGqlError);
Expand Down
62 changes: 61 additions & 1 deletion backend/Testing/ApiTests/GqlMiddlewareTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,30 @@
namespace Testing.ApiTests;

[Trait("Category", "Integration")]
public class GqlMiddlewareTests : IClassFixture<IntegrationFixture>
public class GqlMiddlewareTests : IClassFixture<IntegrationFixture>, IAsyncLifetime
{
private readonly IntegrationFixture _fixture;
private readonly ApiTestBase _adminApiTester;
private string _adminJwt;

public GqlMiddlewareTests(IntegrationFixture fixture)
{
_fixture = fixture;
_adminApiTester = _fixture.AdminApiTester;
_adminJwt = _fixture.AdminJwt;
}

public async Task InitializeAsync()
{
if (_adminJwt != _adminApiTester.CurrJwt)
{
_adminJwt = await _adminApiTester.LoginAs("admin");
}
}

public Task DisposeAsync()
{
return Task.CompletedTask;
}

private async Task<JsonObject> QueryMyProjectsWithMembers()
Expand Down Expand Up @@ -71,4 +86,49 @@ await Task.WhenAll(

projects.Select(p => p.id).ShouldBeSubsetOf(ids);
}

[Fact]
public async Task CanGetProjectThatWasJustAddedToUser()
{
var config = GetNewProjectConfig(isConfidential: true);
await using var project = await RegisterProjectInLexBox(config, _adminApiTester);

await _adminApiTester.LoginAs("editor");
var editorJwt = _adminApiTester.CurrJwt;

await _adminApiTester.ExecuteGql($$"""
query {
projectByCode(code: "{{config.Code}}") {
id
name
}
}
""", expectGqlError: true); // we're not a member yet
editorJwt.ShouldBe(_adminApiTester.CurrJwt); // token wasn't updated

await AddMemberToProject(config, _adminApiTester, "editor", ProjectRole.Editor, _adminJwt);

await _adminApiTester.ExecuteGql($$"""
query {
projectByCode(code: "{{config.Code}}") {
id
name
}
}
""", expectGqlError: true); // we're a member, but didn't query for users, so...
editorJwt.ShouldBe(_adminApiTester.CurrJwt); // token wasn't updated

var response = await _adminApiTester.ExecuteGql($$"""
query {
projectByCode(code: "{{config.Code}}") {
id
name
users {
id
}
}
}
""", expectGqlError: false); // we queried for users, so...
editorJwt.ShouldNotBe(_adminApiTester.CurrJwt); // token was updated
}
}
4 changes: 3 additions & 1 deletion backend/Testing/Fixtures/IntegrationFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class IntegrationFixture : IAsyncLifetime
public static readonly FileInfo TemplateRepoZip = new(TemplateRepoZipName);
public static readonly DirectoryInfo TemplateRepo = new(Path.Join(BasePath, "_template-repo_"));
public ApiTestBase AdminApiTester { get; private set; } = new();
private string? _adminJwt = null;
public string AdminJwt => _adminJwt.ShouldNotBeNull();

static IntegrationFixture()
{
Expand All @@ -31,7 +33,7 @@ public async Task InitializeAsync()
{
Directory.CreateDirectory(BasePath);
InitTemplateRepo();
await AdminApiTester.LoginAs(AdminAuth.Username, AdminAuth.Password);
_adminJwt = await AdminApiTester.LoginAs(AdminAuth.Username, AdminAuth.Password);
}

public Task DisposeAsync()
Expand Down
20 changes: 20 additions & 0 deletions backend/Testing/LexCore/Utils/GqlUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,25 @@ public static void ValidateGqlErrors(JsonObject json, bool expectError = false)
}
}
}
else
{
var foundError = json["errors"] is JsonArray errors && errors.Count > 0;
if (!foundError)
{
if (json["data"] is JsonObject data)
{
foreach (var (_, resultValue) in data)
{
if (resultValue is JsonObject resultObject)
{
foundError = resultObject["errors"] is JsonArray resultErrors && resultErrors.Count > 0;
if (foundError)
break;
}
}
}
}
foundError.ShouldBeTrue();
}
}
}
24 changes: 16 additions & 8 deletions backend/Testing/Services/JwtHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,26 @@ public static async Task<HttpResponseMessage> ExecuteLogin(SendReceiveAuth auth,

public static string GetJwtFromLoginResponse(HttpResponseMessage response)
{
response.EnsureSuccessStatusCode();
var cookies = response.Headers.GetValues("Set-Cookie");
var cookieContainer = new CookieContainer();
cookieContainer.SetCookies(response.RequestMessage!.RequestUri!, cookies.Single());
var authCookie = cookieContainer.GetAllCookies()
.FirstOrDefault(c => c.Name == AuthKernel.AuthCookieName);
authCookie.ShouldNotBeNull();
var jwt = authCookie.Value;
TryGetJwtFromLoginResponse(response, out var jwt);
jwt.ShouldNotBeNullOrEmpty();
return jwt;
}

public static bool TryGetJwtFromLoginResponse(HttpResponseMessage response, out string? jwt)
{
jwt = null;
response.EnsureSuccessStatusCode();
if (response.Headers.TryGetValues("Set-Cookie", out var cookies))
{
var cookieContainer = new CookieContainer();
cookieContainer.SetCookies(response.RequestMessage!.RequestUri!, cookies.Single());
var authCookie = cookieContainer.GetAllCookies()
.FirstOrDefault(c => c.Name == AuthKernel.AuthCookieName);
jwt = authCookie?.Value;
}
return jwt is not null;
}

public static void ClearCookies(SocketsHttpHandler httpClientHandler)
{
foreach (Cookie cookie in httpClientHandler.CookieContainer.GetAllCookies())
Expand Down
5 changes: 3 additions & 2 deletions backend/Testing/Services/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ public static async Task AddMemberToProject(
ProjectConfig config,
ApiTestBase apiTester,
string usernameOrEmail,
ProjectRole role
ProjectRole role,
string? overrideJwt = null
)
{
await apiTester.ExecuteGql($$"""
Expand All @@ -97,7 +98,7 @@ ... on InvalidEmailError {
}
}
}
""");
""", overrideJwt: overrideJwt);
}

public static void ValidateSendReceiveOutput(string srOutput)
Expand Down

0 comments on commit d1d36ff

Please sign in to comment.