Skip to content

Commit 9944bff

Browse files
feature(#20): includes a suite of end-to-end tests covering paginated listings (GET), creation (POST), and updates (PUT) of clients, with validation of authentication, persistence, and API responses.
1 parent a7557a4 commit 9944bff

1 file changed

Lines changed: 196 additions & 0 deletions

File tree

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
namespace HttpsRichardy.Federation.TestSuite.Integration.Endpoints;
2+
3+
public sealed class ClientEndpointTests(IntegrationEnvironmentFixture factory) :
4+
IClassFixture<IntegrationEnvironmentFixture>
5+
{
6+
private readonly Fixture _fixture = new();
7+
8+
[Fact(DisplayName = "[e2e] - when GET /clients should return paginated list of clients")]
9+
public async Task WhenGetClients_ShouldReturnPaginatedListOfClients()
10+
{
11+
/* arrange: resolve required dependencies */
12+
var clientCollection = factory.Services.GetRequiredService<IClientCollection>();
13+
var realmCollection = factory.Services.GetRequiredService<IRealmCollection>();
14+
15+
/* arrange: authenticate user and get access token */
16+
var httpClient = factory.HttpClient.WithRealmHeader("master");
17+
var credentials = new AuthenticationCredentials
18+
{
19+
Username = "federation.testing.user",
20+
Password = "federation.testing.password"
21+
};
22+
23+
var authenticationResponse = await httpClient.PostAsJsonAsync("api/v1/identity/authenticate", credentials);
24+
var authenticationResult = await authenticationResponse.Content.ReadFromJsonAsync<AuthenticationResult>();
25+
26+
Assert.NotNull(authenticationResult);
27+
Assert.NotEmpty(authenticationResult.AccessToken);
28+
29+
httpClient.WithAuthorization(authenticationResult.AccessToken);
30+
31+
/* arrange: insert 5 clients directly into the database */
32+
var realmFilters = RealmFilters.WithSpecifications()
33+
.WithName("master")
34+
.Build();
35+
36+
var realms = await realmCollection.GetRealmsAsync(realmFilters, CancellationToken.None);
37+
var realm = realms.FirstOrDefault();
38+
39+
Assert.NotNull(realm);
40+
41+
var clients = Enumerable.Range(1, 5)
42+
.Select(index => _fixture.Build<Client>()
43+
.With(client => client.Name, $"test-client-{index}")
44+
.With(client => client.ClientId, $"test-client-id-{index}")
45+
.With(client => client.Secret, $"test-client-secret-{index}")
46+
.With(client => client.RealmId, realm.Id)
47+
.With(client => client.IsDeleted, false)
48+
.Create())
49+
.ToList();
50+
51+
await clientCollection.InsertManyAsync(clients);
52+
53+
/* act: send GET request to retrieve clients */
54+
var response = await httpClient.GetAsync("api/v1/clients");
55+
var result = await response.Content.ReadFromJsonAsync<Pagination<ClientScheme>>();
56+
57+
/* assert: response should be 200 OK */
58+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
59+
60+
Assert.NotNull(result);
61+
Assert.NotNull(result.Items);
62+
63+
/* assert: inserted clients should be present in the returned list */
64+
foreach (var client in clients)
65+
{
66+
Assert.Contains(result.Items, item => item.Id == client.Id);
67+
}
68+
}
69+
70+
[Fact(DisplayName = "[e2e] - when POST /clients with valid data should create client successfully")]
71+
public async Task WhenPostClientsWithValidData_ShouldCreateClientSuccessfully()
72+
{
73+
/* arrange: resolve required dependencies */
74+
var clientCollection = factory.Services.GetRequiredService<IClientCollection>();
75+
76+
/* arrange: authenticate user and get access token */
77+
var httpClient = factory.HttpClient.WithRealmHeader("master");
78+
var credentials = new AuthenticationCredentials
79+
{
80+
Username = "federation.testing.user",
81+
Password = "federation.testing.password"
82+
};
83+
84+
var authenticationResponse = await httpClient.PostAsJsonAsync("api/v1/identity/authenticate", credentials);
85+
var authenticationResult = await authenticationResponse.Content.ReadFromJsonAsync<AuthenticationResult>();
86+
87+
Assert.NotNull(authenticationResult);
88+
Assert.NotEmpty(authenticationResult.AccessToken);
89+
90+
httpClient.WithAuthorization(authenticationResult.AccessToken);
91+
92+
/* arrange: prepare request to create a new client */
93+
var payload = _fixture.Build<ClientCreationScheme>()
94+
.With(client => client.Name, $"test-client-{Guid.NewGuid()}")
95+
.With(client => client.Flows, [Grant.ClientCredentials])
96+
.With(client => client.RedirectUris, [])
97+
.Create();
98+
99+
/* act: send POST request to create client */
100+
var response = await httpClient.PostAsJsonAsync("api/v1/clients", payload);
101+
102+
/* assert: response should be 201 Created */
103+
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
104+
105+
/* assert: client must be persisted in the repository */
106+
var filters = ClientFilters.WithSpecifications()
107+
.WithName(payload.Name)
108+
.Build();
109+
110+
var clients = await clientCollection.GetClientsAsync(filters, CancellationToken.None);
111+
var createdClient = clients.FirstOrDefault();
112+
113+
Assert.NotEmpty(clients);
114+
Assert.NotNull(createdClient);
115+
116+
Assert.Equal(payload.Name, createdClient.Name);
117+
Assert.Equal(payload.Flows, createdClient.Flows);
118+
}
119+
120+
[Fact(DisplayName = "[e2e] - when PUT /clients/{id} with valid data should update client successfully")]
121+
public async Task WhenPutClientsWithValidData_ShouldUpdateClientSuccessfully()
122+
{
123+
/* arrange: resolve required dependencies */
124+
var clientCollection = factory.Services.GetRequiredService<IClientCollection>();
125+
126+
/* arrange: authenticate user and get access token */
127+
var httpClient = factory.HttpClient.WithRealmHeader("master");
128+
var credentials = new AuthenticationCredentials
129+
{
130+
Username = "federation.testing.user",
131+
Password = "federation.testing.password"
132+
};
133+
134+
var authenticationResponse = await httpClient.PostAsJsonAsync("api/v1/identity/authenticate", credentials);
135+
var authenticationResult = await authenticationResponse.Content.ReadFromJsonAsync<AuthenticationResult>();
136+
137+
Assert.NotNull(authenticationResult);
138+
Assert.NotEmpty(authenticationResult.AccessToken);
139+
140+
httpClient.WithAuthorization(authenticationResult.AccessToken);
141+
142+
/* arrange: create a new client */
143+
var createPayload = _fixture.Build<ClientCreationScheme>()
144+
.With(client => client.Name, $"test-client-{Guid.NewGuid()}")
145+
.With(client => client.Flows, [Grant.ClientCredentials])
146+
.With(client => client.RedirectUris, [])
147+
.Create();
148+
149+
var createResponse = await httpClient.PostAsJsonAsync("api/v1/clients", createPayload);
150+
151+
Assert.Equal(HttpStatusCode.Created, createResponse.StatusCode);
152+
153+
var filters = ClientFilters.WithSpecifications()
154+
.WithName(createPayload.Name)
155+
.Build();
156+
157+
var clients = await clientCollection.GetClientsAsync(filters, CancellationToken.None);
158+
var client = clients.FirstOrDefault();
159+
160+
Assert.NotEmpty(clients);
161+
Assert.NotNull(client);
162+
163+
/* arrange: prepare request to update client */
164+
var payload = _fixture.Build<ClientUpdateScheme>()
165+
.With(update => update.Name, $"updated-client-{Guid.NewGuid()}")
166+
.With(update => update.Flows, [Grant.AuthorizationCode])
167+
.With(update => update.RedirectUris, ["https://localhost/callback"])
168+
.Create();
169+
170+
/* act: send PUT request to update client */
171+
var response = await httpClient.PutAsJsonAsync($"api/v1/clients/{client.Id}", payload);
172+
var result = await response.Content.ReadFromJsonAsync<ClientScheme>();
173+
174+
/* assert: response should be 200 OK */
175+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
176+
Assert.NotNull(result);
177+
178+
/* assert: client must be updated in the repository */
179+
var clientFilters = ClientFilters.WithSpecifications()
180+
.WithName(payload.Name)
181+
.Build();
182+
183+
var matchingClients = await clientCollection.GetClientsAsync(clientFilters, CancellationToken.None);
184+
var persistedClient = matchingClients.FirstOrDefault(current => current.Id == client.Id);
185+
186+
Assert.NotEmpty(matchingClients);
187+
Assert.NotNull(persistedClient);
188+
189+
Assert.Equal(client.Id, result.Id);
190+
Assert.Equal(payload.Name, result.Name);
191+
Assert.Equal(payload.Name, persistedClient.Name);
192+
Assert.Equal(payload.Flows, persistedClient.Flows);
193+
194+
Assert.Contains(persistedClient.RedirectUris, uri => uri.Address == "https://localhost/callback");
195+
}
196+
}

0 commit comments

Comments
 (0)