Skip to content

Commit 321ed33

Browse files
committed
Tidying things up and attempting a 'get one item' endpoint
1 parent c3841b7 commit 321ed33

File tree

7 files changed

+172
-153
lines changed

7 files changed

+172
-153
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
namespace GuardianClient.Tests;
2+
3+
using Shouldly;
4+
5+
[TestClass]
6+
public class GuardianApiClientTests : TestBase
7+
{
8+
[TestMethod]
9+
public async Task SearchAsyncSmokeTest()
10+
{
11+
var result = await ApiClient.SearchAsync("climate change", pageSize: 5);
12+
13+
result.ShouldNotBeNull("Search result should not be null");
14+
result.Status.ShouldBe("ok", "API response status should be 'ok'");
15+
result.Results.Count.ShouldBeGreaterThan(0, "Should return at least one result");
16+
result.Results.Count.ShouldBeLessThanOrEqualTo(5, "Should not return more than requested page size");
17+
18+
var firstItem = result.Results.First();
19+
firstItem.Id.ShouldNotBeNullOrEmpty("Content item should have an ID");
20+
firstItem.WebTitle.ShouldNotBeNullOrEmpty("Content item should have a title");
21+
firstItem.WebUrl.ShouldNotBeNullOrEmpty("Content item should have a web URL");
22+
firstItem.ApiUrl.ShouldNotBeNullOrEmpty("Content item should have an API URL");
23+
24+
Console.WriteLine($"Found {result.Results.Count} articles about climate change");
25+
Console.WriteLine($"First article: {firstItem.WebTitle}");
26+
Console.WriteLine($"Published: {firstItem.WebPublicationDate}");
27+
}
28+
29+
[TestMethod]
30+
public async Task SearchAsyncWithNoResults()
31+
{
32+
var result = await ApiClient.SearchAsync("xyzabc123nonexistentquery456");
33+
34+
result.ShouldNotBeNull("Search result should not be null even with no matches");
35+
result.Status.ShouldBe("ok", "API response status should be 'ok'");
36+
result.Results.Count.ShouldBe(0, "Should return zero results for non-existent query");
37+
}
38+
39+
[TestMethod]
40+
public async Task GetItemAsyncSmokeTest()
41+
{
42+
var searchResult = await ApiClient.SearchAsync("technology", pageSize: 1);
43+
searchResult.ShouldNotBeNull("Search should return results");
44+
searchResult.Results.Count.ShouldBe(1, "Should return exactly one result");
45+
46+
var contentItem = searchResult.Results.First();
47+
var itemId = contentItem.Id;
48+
var singleItemResult = await ApiClient.GetItemAsync(itemId);
49+
50+
singleItemResult.ShouldNotBeNull("GetItem result should not be null");
51+
singleItemResult.Status.ShouldBe("ok", "API response status should be 'ok'");
52+
singleItemResult.Content.ShouldNotBeNull("Content should not be null");
53+
singleItemResult.Content.Id.ShouldBe(itemId, "Returned content ID should match requested ID");
54+
singleItemResult.Content.WebTitle.ShouldNotBeNullOrEmpty("Content should have a title");
55+
56+
Console.WriteLine($"Retrieved item: {singleItemResult.Content.WebTitle}");
57+
Console.WriteLine($"Item ID: {singleItemResult.Content.Id}");
58+
Console.WriteLine($"Published: {singleItemResult.Content.WebPublicationDate}");
59+
}
60+
}

GuardianClient/GuardianClient.Tests/GuardianClient.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
2020
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
2121
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
22+
<PackageReference Include="Shouldly" Version="4.3.0" />
2223
</ItemGroup>
2324

2425
<ItemGroup>

GuardianClient/GuardianClient.Tests/SearchAsyncTests.cs

Lines changed: 0 additions & 85 deletions
This file was deleted.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using GuardianClient.Extensions;
2+
using Microsoft.Extensions.Configuration;
3+
using Microsoft.Extensions.DependencyInjection;
4+
5+
namespace GuardianClient.Tests;
6+
7+
public abstract class TestBase
8+
{
9+
protected static GuardianApiClient ApiClient { get; }
10+
11+
static TestBase()
12+
{
13+
var config = new ConfigurationBuilder()
14+
.AddUserSecrets<TestBase>()
15+
.Build();
16+
17+
var apiKey = config["GuardianApiKey"]!;
18+
19+
ApiClient = new ServiceCollection()
20+
.AddGuardianApiClient(apiKey)
21+
.BuildServiceProvider()
22+
.GetRequiredService<GuardianApiClient>();
23+
}
24+
}

GuardianClient/GuardianClient/Extensions/ServiceCollectionExtensions.cs

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,24 @@ namespace GuardianClient.Extensions;
55
public static class ServiceCollectionExtensions
66
{
77
/// <summary>
8-
/// Adds GuardianApiClient to the service collection with HttpClient factory support
8+
/// Registers <see cref="GuardianApiClient"/> in the service collection,
9+
/// configuring it to use <see cref="IHttpClientFactory"/> for efficient
10+
/// <see cref="HttpClient"/> management.
911
/// </summary>
10-
/// <param name="services">The service collection</param>
11-
/// <param name="apiKey">Your Guardian API key</param>
12-
/// <returns>The service collection for chaining</returns>
12+
/// <param name="services">The DI service collection.</param>
13+
/// <param name="apiKey">The Guardian API key to use for all requests.</param>
14+
/// <returns>The same <paramref name="services"/> instance for chaining.</returns>
1315
public static IServiceCollection AddGuardianApiClient(this IServiceCollection services, string apiKey)
1416
{
15-
if (string.IsNullOrWhiteSpace(apiKey))
16-
throw new ArgumentException("API key cannot be null or empty", nameof(apiKey));
17+
ArgumentException.ThrowIfNullOrWhiteSpace(apiKey);
1718

18-
// Register the HttpClient with IHttpClientFactory
19-
services.AddHttpClient<GuardianApiClient>(client =>
20-
{
21-
client.BaseAddress = new Uri("https://content.guardianapis.com");
22-
client.DefaultRequestHeaders.Add("User-Agent", "GuardianClient.NET/0.1.0-alpha");
23-
});
19+
services.AddHttpClient<GuardianApiClient>();
2420

25-
// Register GuardianApiClient as scoped
2621
services.AddScoped<GuardianApiClient>(serviceProvider =>
2722
{
2823
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
2924
var httpClient = httpClientFactory.CreateClient(nameof(GuardianApiClient));
30-
return new GuardianApiClient(httpClient, apiKey);
31-
});
32-
33-
return services;
34-
}
3525

36-
/// <summary>
37-
/// Adds GuardianApiClient to the service collection with configuration action
38-
/// </summary>
39-
/// <param name="services">The service collection</param>
40-
/// <param name="apiKey">Your Guardian API key</param>
41-
/// <param name="configureClient">Action to configure the HttpClient</param>
42-
/// <returns>The service collection for chaining</returns>
43-
public static IServiceCollection AddGuardianApiClient(
44-
this IServiceCollection services,
45-
string apiKey,
46-
Action<HttpClient> configureClient)
47-
{
48-
if (string.IsNullOrWhiteSpace(apiKey))
49-
throw new ArgumentException("API key cannot be null or empty", nameof(apiKey));
50-
51-
// Register the HttpClient with IHttpClientFactory and custom configuration
52-
services.AddHttpClient<GuardianApiClient>(client =>
53-
{
54-
client.BaseAddress = new Uri("https://content.guardianapis.com");
55-
client.DefaultRequestHeaders.Add("User-Agent", "GuardianClient.NET/0.1.0-alpha");
56-
57-
// Apply custom configuration
58-
configureClient?.Invoke(client);
59-
});
60-
61-
// Register GuardianApiClient as scoped
62-
services.AddScoped<GuardianApiClient>(serviceProvider =>
63-
{
64-
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
65-
var httpClient = httpClientFactory.CreateClient(nameof(GuardianApiClient));
6626
return new GuardianApiClient(httpClient, apiKey);
6727
});
6828

0 commit comments

Comments
 (0)