148 lines
5.6 KiB
C#
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¤t_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}¤t_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);
|
|
}
|
|
}
|