Skip to content

Commit 66da1cc

Browse files
Add XML comments for improved code documentation
- Added detailed XML comments across services (`AuthService`, `ProjectService`, `JwtProvider`, `UserService`) and repositories (`ProjectRepository`). - Introduced helper and test methods in `UserTest` and `ProjectTest` with comprehensive XML comments for clarity. - Refactored and standardized XML comments for consistent structure and readability.
1 parent d7ec850 commit 66da1cc

File tree

12 files changed

+412
-72
lines changed

12 files changed

+412
-72
lines changed

sparkly-server.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949

5050
<ItemGroup>
5151
<Compile Remove="sparkly-server.test\HealthzTest.cs" />
52-
<Compile Remove="sparkly-server.test\TestWebAppliactionFactory.cs" />
5352
<Compile Remove="sparkly-server.test\UserTest.cs" />
5453
</ItemGroup>
5554

sparkly-server.test/HealthzTest.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
using Sparkly.Tests.Infrastructure;
2-
using System.Net;
1+
using System.Net;
32

4-
namespace sparkly_server.Services.Users.test;
5-
6-
public class HealthzTest : IClassFixture<TestWebApplicationFactory>
3+
namespace sparkly_server.test
74
{
8-
private readonly HttpClient _client;
9-
10-
public HealthzTest(TestWebApplicationFactory factory)
5+
public class HealthzTest : IClassFixture<TestWebApplicationFactory>
116
{
12-
_client = factory.CreateClient();
13-
}
7+
private readonly HttpClient _client;
148

15-
[Fact]
16-
public async Task Healthz_ReturnsOk()
17-
{
18-
var response = await _client.GetAsync("/healthz");
9+
public HealthzTest(TestWebApplicationFactory factory)
10+
{
11+
_client = factory.CreateClient();
12+
}
13+
14+
[Fact]
15+
public async Task Healthz_ReturnsOk()
16+
{
17+
var response = await _client.GetAsync("/healthz");
1918

20-
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
19+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
20+
}
2121
}
2222
}

sparkly-server.test/ProjectTest.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using sparkly_server.DTO.Auth;
3+
using sparkly_server.DTO.Projects;
4+
using sparkly_server.Enum;
5+
using sparkly_server.Infrastructure;
6+
using System.Net.Http.Headers;
7+
using System.Net.Http.Json;
8+
9+
namespace sparkly_server.test
10+
{
11+
public class ProjectTest : IClassFixture<TestWebApplicationFactory>, IAsyncLifetime
12+
{
13+
private readonly HttpClient _client;
14+
private readonly TestWebApplicationFactory _factory;
15+
16+
public ProjectTest(TestWebApplicationFactory factory)
17+
{
18+
_factory = factory;
19+
_client = factory.CreateClient();
20+
}
21+
22+
public async Task InitializeAsync()
23+
{
24+
using var scope = _factory.Services.CreateScope();
25+
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
26+
27+
db.Users.RemoveRange(db.Users);
28+
db.Projects.RemoveRange(db.Projects);
29+
await db.SaveChangesAsync();
30+
}
31+
32+
public Task DisposeAsync() => Task.CompletedTask;
33+
34+
// Helpers
35+
36+
/// <summary>
37+
/// Creates a new project asynchronously with the specified project name and returns the created project's details.
38+
/// </summary>
39+
/// <param name="projectName">The name of the project to be created.</param>
40+
/// <returns>A task representing the asynchronous operation. The result contains the details of the created project, or null if deserialization fails.</returns>
41+
private async Task<ProjectResponse?> CreateProjectAsync(string projectName)
42+
{
43+
var payload = new CreateProjectRequest(
44+
ProjectName: projectName,
45+
Description: "Test project",
46+
Visibility: ProjectVisibility.Public
47+
);
48+
49+
var response = await _client.PostAsJsonAsync("/api/v1/projects/create", payload);
50+
response.EnsureSuccessStatusCode();
51+
52+
var created = await response.Content.ReadFromJsonAsync<ProjectResponse>();
53+
return created;
54+
}
55+
56+
/// <summary>
57+
/// Registers a new user and logs them in, setting the authentication token in the HTTP client header for subsequent requests.
58+
/// </summary>
59+
/// <exception cref="InvalidOperationException">Thrown when the authentication response does not contain an access token.</exception>
60+
/// <returns>A task that represents the asynchronous operation of registering and logging in a user.</returns>
61+
private async Task RegisterAndLoginUser()
62+
{
63+
var email = "[email protected]";
64+
var password = "Test1234!";
65+
var userName = "testuser";
66+
67+
var registerPayload = new RegisterRequest(Username:userName, Email: email, Password: password);
68+
var registerResponse = await _client.PostAsJsonAsync("/api/v1/auth/register", registerPayload);
69+
registerResponse.EnsureSuccessStatusCode();
70+
71+
var loginPayload = new LoginRequest(Identifier: email, Password: password);
72+
var loginResponse = await _client.PostAsJsonAsync("/api/v1/auth/login", loginPayload);
73+
74+
loginResponse.EnsureSuccessStatusCode();
75+
76+
var loginContent = await loginResponse.Content.ReadFromJsonAsync<AuthResponse>();
77+
78+
if (loginContent?.AccessToken is null)
79+
{
80+
throw new InvalidOperationException("Auth response did not contain access token");
81+
}
82+
83+
_client.DefaultRequestHeaders.Authorization =
84+
new AuthenticationHeaderValue("Bearer", loginContent.AccessToken);
85+
}
86+
87+
// Tests
88+
[Fact]
89+
public async Task CreateProject_Should_Create_Project_For_Authenticated_User()
90+
{
91+
await RegisterAndLoginUser();
92+
var projectName = "MyTestProject";
93+
94+
var created = await CreateProjectAsync(projectName);
95+
96+
Assert.NotNull(created);
97+
Assert.Equal(projectName, created.ProjectName);
98+
}
99+
}
100+
}

sparkly-server.test/TestWebAppliactionFactory.cs

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,47 @@
55
using Microsoft.Extensions.DependencyInjection;
66
using sparkly_server.Infrastructure;
77

8-
namespace Sparkly.Tests.Infrastructure;
9-
10-
public class TestWebApplicationFactory : WebApplicationFactory<Program>
8+
namespace sparkly_server.test
119
{
12-
protected override void ConfigureWebHost(IWebHostBuilder builder)
10+
public class TestWebApplicationFactory : WebApplicationFactory<Program>
1311
{
14-
builder.UseEnvironment("Testing");
15-
16-
builder.ConfigureAppConfiguration((config) =>
12+
protected override void ConfigureWebHost(IWebHostBuilder builder)
1713
{
18-
var settings = new Dictionary<string, string?>
14+
builder.UseEnvironment("Testing");
15+
16+
builder.ConfigureAppConfiguration((config) =>
1917
{
20-
["SPARKLY_JWT_KEY"] = "this-is-very-long-test-jwt-key-123456",
21-
["SPARKLY_JWT_ISSUER"] = "sparkly-test-issuer"
22-
};
18+
var settings = new Dictionary<string, string?>
19+
{
20+
["SPARKLY_JWT_KEY"] = "this-is-very-long-test-jwt-key-123456",
21+
["SPARKLY_JWT_ISSUER"] = "sparkly-test-issuer"
22+
};
2323

24-
config.AddInMemoryCollection(settings);
25-
});
26-
27-
builder.ConfigureServices(services =>
28-
{
29-
var descriptor = services.SingleOrDefault(
30-
d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
24+
config.AddInMemoryCollection(settings);
25+
});
3126

32-
if (descriptor is not null)
27+
builder.ConfigureServices(services =>
3328
{
34-
services.Remove(descriptor);
35-
}
29+
var descriptor = services.SingleOrDefault(
30+
d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
3631

37-
services.AddDbContext<AppDbContext>(options =>
38-
{
39-
options.UseInMemoryDatabase("sparkly-tests");
40-
});
32+
if (descriptor is not null)
33+
{
34+
services.Remove(descriptor);
35+
}
4136

42-
var sp = services.BuildServiceProvider();
37+
services.AddDbContext<AppDbContext>(options =>
38+
{
39+
options.UseInMemoryDatabase("sparkly-tests");
40+
});
4341

44-
using var scope = sp.CreateScope();
45-
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
46-
db.Database.EnsureDeleted();
47-
db.Database.EnsureCreated();
48-
});
42+
var sp = services.BuildServiceProvider();
43+
44+
using var scope = sp.CreateScope();
45+
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
46+
db.Database.EnsureDeleted();
47+
db.Database.EnsureCreated();
48+
});
49+
}
4950
}
5051
}

sparkly-server.test/UserTest.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
using Microsoft.Extensions.DependencyInjection;
22
using sparkly_server.DTO.Auth;
33
using sparkly_server.Infrastructure;
4-
using Sparkly.Tests.Infrastructure;
54
using System.Text;
65
using System.Text.Json;
76

8-
namespace sparkly_server.Services.Users.test
7+
namespace sparkly_server.test
98
{
109
public class UserTest : IClassFixture<TestWebApplicationFactory>, IAsyncLifetime
1110
{
@@ -29,7 +28,16 @@ public async Task InitializeAsync()
2928
}
3029

3130
public Task DisposeAsync() => Task.CompletedTask;
32-
31+
32+
/// <summary>
33+
/// Registers a test user in the system by sending a registration request to the API.
34+
/// </summary>
35+
/// <param name="userName">The username of the user to register.</param>
36+
/// <param name="email">The email address of the user to register.</param>
37+
/// <param name="password">The password of the user to register.</param>
38+
/// <returns>A task that represents the asynchronous operation.</returns>
39+
40+
// Helper
3341
private async Task RegisterTestUserAsync(string userName, string email, string password)
3442
{
3543
var payload = new RegisterRequest(Username: userName, Email: email, Password: password);
@@ -44,6 +52,7 @@ private async Task RegisterTestUserAsync(string userName, string email, string p
4452
response.EnsureSuccessStatusCode();
4553
}
4654

55+
// Tests
4756
[Fact]
4857
public async Task User_CanBeCreated()
4958
{

src/Domain/Auth/RefreshToken.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public RefreshToken(Guid userId, string token, DateTime expiresAt)
2727
ExpiresAt = expiresAt;
2828
}
2929

30+
/// <summary>
31+
/// Revokes the refresh token by marking it as no longer active.
32+
/// </summary>
33+
/// <param name="ip">The IP address from which the revoke operation is made.</param>
34+
/// <param name="replacedByToken">An optional new token that replaces the current token.</param>
3035
public void Revoke(string? ip = null, string? replacedByToken = null)
3136
{
3237
if (RevokedAt is not null)

src/Domain/Projects/Project.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ private static string NormalizeSlug(string value)
9393
public void Rename(string newName)
9494
{
9595
SetNameInternal(newName);
96-
// Jeśli chcesz, aby slug szedł za nazwą:
9796
Slug = NormalizeSlug(newName);
9897
Touch();
9998
}

src/Services/Auth/AuthService.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ public AuthService(IUserService userService,
2020
_db = db;
2121
}
2222

23+
/// <summary>
24+
/// Authenticates a user based on the provided credentials and generates an authentication result containing tokens.
25+
/// </summary>
26+
/// <param name="identifier">The user's identifier, such as username or email address.</param>
27+
/// <param name="password">The user's password.</param>
28+
/// <param name="ct">A cancellation token to observe while awaiting the task.</param>
29+
/// <returns>An AuthResult object containing the access token, refresh token, and their expiry times, or null if authentication fails.</returns>
2330
public async Task<AuthResult?> LoginAsync(string identifier, string password, CancellationToken ct = default)
2431
{
2532
var user = await _userService.ValidateUserAsync(identifier, password, ct);
@@ -51,6 +58,12 @@ public AuthService(IUserService userService,
5158
);
5259
}
5360

61+
/// <summary>
62+
/// Refreshes the user's authentication tokens by validating the provided refresh token and generating a new access token.
63+
/// </summary>
64+
/// <param name="refreshToken">The existing refresh token issued to the user for renewing authentication.</param>
65+
/// <param name="ct">A cancellation token to observe while performing the refresh operation.</param>
66+
/// <returns>An AuthResult object containing the new access token, the provided refresh token, and their respective expiry times. Returns null if the refresh token is invalid or inactive.</returns>
5467
public async Task<AuthResult> RefreshAsync(string refreshToken, CancellationToken ct = default)
5568
{
5669
if (string.IsNullOrWhiteSpace(refreshToken))
@@ -81,6 +94,12 @@ public async Task<AuthResult> RefreshAsync(string refreshToken, CancellationToke
8194
);
8295
}
8396

97+
/// <summary>
98+
/// Revokes a refresh token to log the user out by marking the token as revoked in the database.
99+
/// </summary>
100+
/// <param name="refreshToken">The token to be revoked, which identifies the user session.</param>
101+
/// <param name="ct">A cancellation token to observe while awaiting the task.</param>
102+
/// <returns>A Task representing the asynchronous operation.</returns>
84103
public async Task LogoutAsync(string refreshToken, CancellationToken ct = default)
85104
{
86105
if (string.IsNullOrWhiteSpace(refreshToken))

src/Services/Auth/JwtProvider.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ public JwtProvider(IConfiguration config)
2020
_audience = config["SPARKLY_JWT_AUDIENCE"] ?? "sparkly-api";
2121
}
2222

23+
/// <summary>
24+
/// Generates a JWT access token for the given user.
25+
/// </summary>
26+
/// <param name="user">The user for whom the access token is being generated. The user object should contain details like Id, Email, UserName, and Role.</param>
27+
/// <returns>A string representing the generated JWT access token.</returns>
2328
public string GenerateAccessToken(User user)
2429
{
2530
var claims = new List<Claim>
@@ -46,6 +51,10 @@ public string GenerateAccessToken(User user)
4651
return new JwtSecurityTokenHandler().WriteToken(token);
4752
}
4853

54+
/// <summary>
55+
/// Generates a secure refresh token to be used for renewing access tokens.
56+
/// </summary>
57+
/// <returns>A string representing the generated refresh token.</returns>
4958
public string GenerateRefreshToken()
5059
{
5160
// na razie prosty generator; później można dodać zapisywanie do bazy

0 commit comments

Comments
 (0)