refactor: remove deploy.cmd and update Dockerfile for health checks
ci/woodpecker/push/01-test Pipeline failed
ci/woodpecker/push/02-build-push unknown status

- Deleted the deploy.cmd script as it was no longer needed.
- Updated Dockerfile to include curl for health checks and added a non-root user for improved security.
- Modified health check command to use curl for better reliability.
- Adjusted docker-compose.test.yml to reflect changes in health check configuration.
- Cleaned up appsettings.json and removed unused configuration properties.
- Removed Resource entity and related requests from the codebase as part of the architectural shift.
- Updated documentation to reflect the removal of hardware binding and related endpoints.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-13 08:47:21 +03:00
parent 43fe38e67d
commit c7b297de83
76 changed files with 4034 additions and 832 deletions
-176
View File
@@ -1,176 +0,0 @@
using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using Azaion.E2E.Helpers;
using FluentAssertions;
using Xunit;
namespace Azaion.E2E.Tests;
[Collection("E2E")]
public sealed class ResourceUpdateTests
{
private static readonly JsonSerializerOptions ResponseJsonOptions = new()
{
PropertyNameCaseInsensitive = true
};
private sealed record ResourceUpdateItemDto(
string ResourceName,
string Version,
string CdnUrl,
string Sha256,
string EncryptionKey,
long SizeBytes);
private readonly TestFixture _fixture;
public ResourceUpdateTests(TestFixture fixture) => _fixture = fixture;
private static object PublishBody(string resourceName, string version, string arch = "arm64",
string stage = "stage", string encryptionKey = "test-resource-key-001") => new
{
resourceName,
devStage = stage,
architecture = arch,
version,
cdnUrl = $"https://cdn.example.com/{resourceName}-{version}.bin",
sha256 = "abc123def456789",
encryptionKey,
sizeBytes = 1024L
};
private async Task<string> NewUploaderTokenAsync()
{
using var loginClient = _fixture.CreateApiClient();
return await loginClient.LoginAsync(_fixture.UploaderEmail, _fixture.UploaderPassword);
}
[Fact]
public async Task AC2_GetUpdate_returns_resources_newer_than_device_version()
{
// Arrange
var uploaderToken = await NewUploaderTokenAsync();
using var uploaderClient = _fixture.CreateAuthenticatedClient(uploaderToken);
using var deviceClient = _fixture.CreateAuthenticatedClient(_fixture.AdminToken);
var arch = "arm64";
var stage = $"stage-{Guid.NewGuid():N}".Substring(0, 12);
var resourceName = $"annotations-{Guid.NewGuid():N}".Substring(0, 20);
using var publish = await uploaderClient.PostAsync("/resources/publish",
PublishBody(resourceName, "2026-04-13", arch, stage, "device-key-AC2"));
publish.StatusCode.Should().Be(HttpStatusCode.OK);
// Act
using var response = await deviceClient.PostAsync("/get-update", new
{
architecture = arch,
devStage = stage,
currentVersions = new Dictionary<string, string> { [resourceName] = "2026-02-25" }
});
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var items = await response.Content.ReadFromJsonAsync<List<ResourceUpdateItemDto>>(ResponseJsonOptions);
items.Should().NotBeNull();
items!.Should().HaveCount(1);
items![0].ResourceName.Should().Be(resourceName);
items[0].Version.Should().Be("2026-04-13");
items[0].CdnUrl.Should().Be($"https://cdn.example.com/{resourceName}-2026-04-13.bin");
items[0].Sha256.Should().Be("abc123def456789");
items[0].EncryptionKey.Should().Be("device-key-AC2",
"the column is AES-encrypted at rest but the response must contain plaintext for the device");
items[0].SizeBytes.Should().Be(1024L);
}
[Fact]
public async Task AC3_GetUpdate_returns_empty_when_device_already_has_latest()
{
// Arrange
var uploaderToken = await NewUploaderTokenAsync();
using var uploaderClient = _fixture.CreateAuthenticatedClient(uploaderToken);
using var deviceClient = _fixture.CreateAuthenticatedClient(_fixture.AdminToken);
var arch = "arm64";
var stage = $"stage-{Guid.NewGuid():N}".Substring(0, 12);
var resourceName = $"weights-{Guid.NewGuid():N}".Substring(0, 20);
using var publish = await uploaderClient.PostAsync("/resources/publish",
PublishBody(resourceName, "2026-04-13", arch, stage));
publish.StatusCode.Should().Be(HttpStatusCode.OK);
// Act
using var response = await deviceClient.PostAsync("/get-update", new
{
architecture = arch,
devStage = stage,
currentVersions = new Dictionary<string, string> { [resourceName] = "2026-04-13" }
});
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var items = await response.Content.ReadFromJsonAsync<List<ResourceUpdateItemDto>>(ResponseJsonOptions);
items.Should().NotBeNull();
items!.Should().BeEmpty();
}
[Fact]
public async Task AC5_Cache_is_invalidated_on_publish()
{
// Arrange
var uploaderToken = await NewUploaderTokenAsync();
using var uploaderClient = _fixture.CreateAuthenticatedClient(uploaderToken);
using var deviceClient = _fixture.CreateAuthenticatedClient(_fixture.AdminToken);
var arch = "arm64";
var stage = $"stage-{Guid.NewGuid():N}".Substring(0, 12);
var resourceName = $"models-{Guid.NewGuid():N}".Substring(0, 20);
using var publishV1 = await uploaderClient.PostAsync("/resources/publish",
PublishBody(resourceName, "2026-02-25", arch, stage));
publishV1.StatusCode.Should().Be(HttpStatusCode.OK);
var deviceVersionsAtV1 = new { architecture = arch, devStage = stage,
currentVersions = new Dictionary<string, string> { [resourceName] = "2026-02-25" } };
using (var primeCache = await deviceClient.PostAsync("/get-update", deviceVersionsAtV1))
{
primeCache.StatusCode.Should().Be(HttpStatusCode.OK);
var primed = await primeCache.Content.ReadFromJsonAsync<List<ResourceUpdateItemDto>>(ResponseJsonOptions);
primed!.Should().BeEmpty();
}
// Act
using var publishV2 = await uploaderClient.PostAsync("/resources/publish",
PublishBody(resourceName, "2026-04-13", arch, stage));
publishV2.StatusCode.Should().Be(HttpStatusCode.OK);
using var afterPublish = await deviceClient.PostAsync("/get-update", deviceVersionsAtV1);
// Assert
afterPublish.StatusCode.Should().Be(HttpStatusCode.OK);
var items = await afterPublish.Content.ReadFromJsonAsync<List<ResourceUpdateItemDto>>(ResponseJsonOptions);
items.Should().NotBeNull();
items!.Should().HaveCount(1, "publish must invalidate the per-(arch,stage) latest-versions cache");
items![0].Version.Should().Be("2026-04-13");
}
[Fact]
public async Task GetUpdate_without_jwt_returns_401()
{
// Arrange
using var client = _fixture.CreateApiClient();
// Act
using var response = await client.PostAsync("/get-update", new
{
architecture = "arm64",
devStage = "stage",
currentVersions = new Dictionary<string, string>()
});
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
}
}