thecr3w-dev/tests/TheCr3w.Tests/ApiIntegrationTests.cs

148 lines
5.6 KiB
C#

using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using TheCr3w.Shared.Crypto;
using TheCr3w.Shared.Data;
using TheCr3w.Shared.Models;
using TheCr3w.Shared.Storage;
using Xunit;
namespace TheCr3w.Tests;
public class TestR2StorageService : IR2StorageService
{
public string GeneratePreSignedDownloadUrl(string objectKey, TimeSpan? duration = null)
{
return $"https://bf705752e38169fedb83efd5b747390e.r2.cloudflarestorage.com/thecr3w-app/{objectKey}?token=mock-sigv4-token";
}
public string GeneratePreSignedUploadUrl(string objectKey, string contentType, TimeSpan? duration = null)
{
return $"https://bf705752e38169fedb83efd5b747390e.r2.cloudflarestorage.com/thecr3w-app/{objectKey}?upload=true";
}
public Task<bool> ObjectExistsAsync(string objectKey, CancellationToken cancellationToken = default)
{
return Task.FromResult(true);
}
}
public class NativeUpdateApiFactory : WebApplicationFactory<NativeUpdate.Api.Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Testing");
builder.ConfigureServices(services =>
{
services.AddDbContext<TheCr3wDbContext>(options =>
{
options.UseInMemoryDatabase("TheCr3w_Test_Db");
});
// Podmiana R2 na mock testowy
var r2Descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IR2StorageService));
if (r2Descriptor != null)
services.Remove(r2Descriptor);
services.AddSingleton<IR2StorageService, TestR2StorageService>();
});
}
}
public class ApiIntegrationTests : IClassFixture<NativeUpdateApiFactory>
{
private readonly HttpClient _client;
private readonly NativeUpdateApiFactory _factory;
public ApiIntegrationTests(NativeUpdateApiFactory factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async Task HealthEndpoint_ShouldReturnHealthy()
{
var response = await _client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
Assert.Contains("Healthy", body);
}
[Fact]
public async Task GetSigningPublicKey_ShouldReturnValidEd25519Key()
{
var response = await _client.GetAsync("/api/v1/keys/signing-public-key");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var json = await response.Content.ReadFromJsonAsync<JsonElement>();
Assert.Equal("Ed25519", json.GetProperty("algorithm").GetString());
var keyHex = json.GetProperty("public_key_hex").GetString();
Assert.NotNull(keyHex);
Assert.Equal(64, keyHex.Length);
}
[Fact]
public async Task CheckUpdate_WhenUpToDate_ShouldReturnFalse()
{
var response = await _client.GetAsync("/api/v1/updates/check?app_id=com.thecr3w.uptodate&current_version=1.0.0&version_code=10&platform=android&architecture=arm64-v8a&channel=stable");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var json = await response.Content.ReadFromJsonAsync<JsonElement>();
Assert.False(json.GetProperty("update_available").GetBoolean());
}
[Fact]
public async Task CheckUpdate_WhenNewVersionPublished_ShouldReturnUpdateAndPresignedUrl()
{
var appId = "com.thecr3w.needsupdate";
using (var scope = _factory.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<TheCr3wDbContext>();
var (priv, pub) = Ed25519Service.GenerateKeyPair();
var newRelease = new ReleaseEntity
{
ReleaseId = Guid.NewGuid(),
AppId = appId,
Version = "2.0.0",
VersionCode = 20,
Platform = "android",
Architecture = "arm64-v8a",
Channel = "stable",
Status = "published",
Mandatory = true,
MinimumSupportedVersion = "1.5.0",
FileSizeBytes = 25000000,
Sha256 = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
R2ObjectKey = "apps/com.thecr3w.needsupdate/android/stable/2.0.0_20_arm64-v8a.apk",
SignatureEd25519 = "mock_ed25519_signature",
PublishedAt = DateTimeOffset.UtcNow,
CreatedBy = "release-bot"
};
db.Releases.Add(newRelease);
await db.SaveChangesAsync();
}
var response = await _client.GetAsync($"/api/v1/updates/check?app_id={appId}&current_version=1.0.0&version_code=10&platform=android&architecture=arm64-v8a&channel=stable");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var json = await response.Content.ReadFromJsonAsync<JsonElement>();
Assert.True(json.GetProperty("update_available").GetBoolean());
Assert.Equal("2.0.0", json.GetProperty("latest_version").GetString());
Assert.Equal(20, json.GetProperty("version_code").GetInt32());
Assert.True(json.GetProperty("mandatory").GetBoolean());
var downloadUrl = json.GetProperty("download_url").GetString();
Assert.NotNull(downloadUrl);
Assert.Contains("bf705752e38169fedb83efd5b747390e.r2.cloudflarestorage.com", downloadUrl);
Assert.Contains("thecr3w-app", downloadUrl);
}
}