mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-10-08 14:05:02 -04:00
test(intelligence): enforce beta readiness gates
This commit is contained in:
3 files changed
+131
-2
No files matched your search
@@ -1,3 +1,5 @@
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using allstarr.Controllers;
|
||||
using allstarr.Core.Intelligence;
|
||||
@@ -7,6 +9,7 @@ using allstarr.Services.Admin;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
|
||||
namespace allstarr.Tests;
|
||||
|
||||
@@ -14,11 +17,13 @@ public sealed class IntelligenceControllerTests : IAsyncLifetime
|
||||
{
|
||||
private readonly Guid _tenant = Guid.CreateVersion7(); private readonly Guid _user = Guid.CreateVersion7();
|
||||
private PostgresTestDatabase _database = null!;
|
||||
private readonly CommandCounter _commands = new();
|
||||
private Factory _factory = null!; private FakePolicy _policy = null!; private FakeRuns _runs = null!; private FakeSmart _smart = null!; private FakeReadiness _readiness = null!;
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_database = await PostgresTestDatabase.CreateAsync();
|
||||
_factory = new(_database.Options);
|
||||
_factory = new(new DbContextOptionsBuilder<AllstarrDbContext>(_database.Options)
|
||||
.AddInterceptors(_commands).Options);
|
||||
await using var db = await _factory.CreateDbContextAsync(); await db.Database.MigrateAsync();
|
||||
db.Tenants.Add(new() { Id = _tenant, Slug = "intelligence", Name = "Intelligence", CreatedAt = DateTimeOffset.UtcNow });
|
||||
db.Users.Add(new()
|
||||
@@ -236,7 +241,12 @@ public sealed class IntelligenceControllerTests : IAsyncLifetime
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
var result = Assert.IsType<OkObjectResult>(await Controller().Get(Scope(), default)); var json = JsonSerializer.Serialize(result.Value);
|
||||
_commands.Reset(); var elapsed = Stopwatch.StartNew();
|
||||
var result = Assert.IsType<OkObjectResult>(await Controller().Get(Scope(), default));
|
||||
elapsed.Stop(); var json = JsonSerializer.Serialize(result.Value);
|
||||
Assert.InRange(_commands.Count, 1, 12);
|
||||
Assert.True(elapsed.Elapsed < TimeSpan.FromSeconds(2),
|
||||
$"Intelligence read took {elapsed.Elapsed.TotalMilliseconds:F0} ms.");
|
||||
Assert.Contains("Shared listening context", json); Assert.Contains("Private preview", json); Assert.Contains("visualization", json);
|
||||
Assert.Contains("lastfm:fixture", json, StringComparison.Ordinal);
|
||||
Assert.Contains("Recommended song", json, StringComparison.Ordinal);
|
||||
@@ -481,6 +491,19 @@ public sealed class IntelligenceControllerTests : IAsyncLifetime
|
||||
public async Task DisposeAsync() => await _database.DisposeAsync();
|
||||
private sealed class Factory(DbContextOptions<AllstarrDbContext> options) : IDbContextFactory<AllstarrDbContext>
|
||||
{ public AllstarrDbContext CreateDbContext() => new(options); public Task<AllstarrDbContext> CreateDbContextAsync(CancellationToken cancellationToken = default) => Task.FromResult(CreateDbContext()); }
|
||||
private sealed class CommandCounter : DbCommandInterceptor
|
||||
{
|
||||
private int _count;
|
||||
public int Count => Volatile.Read(ref _count);
|
||||
public void Reset() => Interlocked.Exchange(ref _count, 0);
|
||||
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
|
||||
DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Interlocked.Increment(ref _count);
|
||||
return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
|
||||
}
|
||||
}
|
||||
private sealed class FakePolicy : IIntelligencePolicyService
|
||||
{
|
||||
public IntelligencePolicyRecord? Record { get; set; }
|
||||
|
||||
@@ -317,6 +317,60 @@ public sealed class IntelligenceCoreTests : IAsyncLifetime
|
||||
Assert.Equal(["library:11111111111111111111111111111111"], provider.Seeds);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RankingIsDeterministicAndKeepsTheBestDuplicateEvidence()
|
||||
{
|
||||
await _policies.SetAsync(_scope, new(true, 30, ["play"], ["ranking"]));
|
||||
await new RecommendationRunService(_factory, _jobs, _clock).EnqueueAsync(
|
||||
_scope, [], 10, "ranking-run");
|
||||
var claim = await _jobs.ClaimNextAsync("ranking-worker", ["recommendation.generate"]);
|
||||
var result = await new RecommendationRunJobHandler(_factory, [new RankingProvider()],
|
||||
new ListeningProfileService(_factory, _clock), _clock)
|
||||
.ExecuteAsync(new(claim!, EmptyServices.Instance), default);
|
||||
Assert.Equal(DurableJobCompletionKind.Succeeded, result.Kind);
|
||||
|
||||
await using var db = await _factory.CreateDbContextAsync();
|
||||
var candidates = await db.RecommendationCandidates.OrderBy(item => item.Position).ToListAsync();
|
||||
Assert.Equal(["track-two", "track-one"], candidates.Select(item => item.TrackKey));
|
||||
Assert.Equal([.9, .7], candidates.Select(item => item.Score));
|
||||
Assert.Contains("best duplicate", candidates[0].SignalsJson, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RecommendationAccountsPreferExactUserAndNeverCrossOwnerOrLibrary()
|
||||
{
|
||||
var otherUser = Guid.CreateVersion7();
|
||||
var userAccount = Guid.CreateVersion7();
|
||||
var libraryAccount = Guid.CreateVersion7();
|
||||
await using (var db = await _factory.CreateDbContextAsync())
|
||||
{
|
||||
db.Users.Add(new()
|
||||
{
|
||||
Id = otherUser, TenantId = _tenant, DisplayName = "Other",
|
||||
Status = PlatformUserStatus.Active, CreatedAt = _clock.UtcNow,
|
||||
UpdatedAt = _clock.UtcNow
|
||||
});
|
||||
db.ProviderAccounts.AddRange(
|
||||
Account(userAccount, ProviderAccountScope.User, _user),
|
||||
Account(Guid.CreateVersion7(), ProviderAccountScope.User, otherUser),
|
||||
Account(libraryAccount, ProviderAccountScope.Library, null, "music"),
|
||||
Account(Guid.CreateVersion7(), ProviderAccountScope.Library, null, "other"));
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
var accessor = new ScopedRecommendationAccountAccessor(_factory, null!);
|
||||
Assert.Equal(userAccount, (await accessor.FindAccountAsync(_scope, "fixture", default))!.AccountId);
|
||||
|
||||
await using (var db = await _factory.CreateDbContextAsync())
|
||||
{
|
||||
(await db.ProviderAccounts.SingleAsync(item => item.Id == userAccount)).Enabled = false;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
Assert.Equal(libraryAccount, (await accessor.FindAccountAsync(_scope, "fixture", default))!.AccountId);
|
||||
Assert.Null(await accessor.FindAccountAsync(
|
||||
_scope with { OwnerUserId = Guid.CreateVersion7(), LibraryScopeId = "missing" },
|
||||
"fixture", default));
|
||||
}
|
||||
|
||||
public async Task DisposeAsync() => await _database.DisposeAsync();
|
||||
private sealed class FixtureProvider : IRecommendationProvider
|
||||
{
|
||||
@@ -359,6 +413,23 @@ public sealed class IntelligenceCoreTests : IAsyncLifetime
|
||||
RecommendationProviderState.Succeeded, []));
|
||||
}
|
||||
}
|
||||
private sealed class RankingProvider : IRecommendationProvider
|
||||
{
|
||||
public string Id => "ranking";
|
||||
public Task<RecommendationProviderResult> RecommendAsync(RecommendationRequest request) =>
|
||||
Task.FromResult(new RecommendationProviderResult(RecommendationProviderState.Succeeded,
|
||||
[
|
||||
Candidate("track-one", .7,
|
||||
Guid.Parse("11111111-1111-1111-1111-111111111111"), "tie"),
|
||||
Candidate("track-two", .7,
|
||||
Guid.Parse("22222222-2222-2222-2222-222222222222"), "lower duplicate"),
|
||||
Candidate("track-two", .9,
|
||||
Guid.Parse("22222222-2222-2222-2222-222222222222"), "best duplicate")
|
||||
]));
|
||||
private static RecommendationCandidate Candidate(string key, double score, Guid id, string reason) =>
|
||||
new(key, score, "ranking", [new("score", score, reason)],
|
||||
new(LibraryTrackId: id)) { CanonicalRecordingId = id, SourceRevision = "ranking:1" };
|
||||
}
|
||||
private sealed class Clock(DateTimeOffset now) : IPlatformClock { public DateTimeOffset UtcNow { get; set; } = now; }
|
||||
private sealed class Factory(DbContextOptions<AllstarrDbContext> options) : IDbContextFactory<AllstarrDbContext>
|
||||
{ public AllstarrDbContext CreateDbContext() => new(options); public Task<AllstarrDbContext> CreateDbContextAsync(CancellationToken cancellationToken = default) => Task.FromResult(CreateDbContext()); }
|
||||
@@ -389,4 +460,11 @@ public sealed class IntelligenceCoreTests : IAsyncLifetime
|
||||
Id = id, TenantId = _tenant, CreatedByUserId = _user,
|
||||
CreatedAt = _clock.UtcNow, UpdatedAt = _clock.UtcNow, Revision = 1
|
||||
};
|
||||
private ProviderAccountRecord Account(Guid id, ProviderAccountScope scope,
|
||||
Guid? owner, string? library = null) => new()
|
||||
{
|
||||
Id = id, TenantId = _tenant, OwnerUserId = owner, ProviderId = "fixture",
|
||||
DisplayName = "Fixture", Scope = scope, LibraryScopeId = library, Enabled = true,
|
||||
CreatedAt = _clock.UtcNow, UpdatedAt = _clock.UtcNow, Revision = 1
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace allstarr.Tests;
|
||||
|
||||
public sealed class IntelligenceThirdPartyNoticeContractTests
|
||||
{
|
||||
[Fact]
|
||||
public void ExternalIntelligenceStaysOutsideTheDistributionAndBundledNoticesExist()
|
||||
{
|
||||
var root = FindRepositoryRoot();
|
||||
var sdk = File.ReadAllText(Path.Combine(root, "docs", "extensions", "sdk-v1.md"));
|
||||
|
||||
Assert.Contains("service implementation remain outside the Allstarr package", sdk,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("not a bundled registry or third-party extension packages", sdk,
|
||||
StringComparison.Ordinal);
|
||||
Assert.True(File.Exists(Path.Combine(root, "allstarr", "wwwroot", "vendor", "LICENSE.lit")));
|
||||
Assert.True(File.Exists(Path.Combine(root, "allstarr", "wwwroot", "licenses", "fonts", "Inter-OFL.txt")));
|
||||
Assert.True(File.Exists(Path.Combine(root, "allstarr", "wwwroot", "licenses", "fonts", "Sora-OFL.txt")));
|
||||
}
|
||||
|
||||
private static string FindRepositoryRoot()
|
||||
{
|
||||
var directory = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "allstarr.sln")))
|
||||
directory = directory.Parent;
|
||||
return directory?.FullName ??
|
||||
throw new DirectoryNotFoundException("Could not locate repository root.");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user