Add integration tests for migration categories and execution
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled

- Implemented MigrationCategoryTests to validate migration categorization for startup, release, seed, and data migrations.
- Added tests for edge cases, including null, empty, and whitespace migration names.
- Created StartupMigrationHostTests to verify the behavior of the migration host with real PostgreSQL instances using Testcontainers.
- Included tests for migration execution, schema creation, and handling of pending release migrations.
- Added SQL migration files for testing: creating a test table, adding a column, a release migration, and seeding data.
This commit is contained in:
master
2025-12-04 19:10:54 +02:00
parent 600f3a7a3c
commit 75f6942769
301 changed files with 32810 additions and 1128 deletions

View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Auth.Client;
namespace StellaOps.Cli.Extensions;
/// <summary>
/// Extension methods for IStellaOpsTokenClient providing compatibility with older CLI patterns.
/// These bridge the gap between the old API (GetTokenAsync, GetAccessTokenAsync) and the
/// new API (RequestClientCredentialsTokenAsync, GetCachedTokenAsync).
/// </summary>
public static class StellaOpsTokenClientExtensions
{
/// <summary>
/// Requests an access token using client credentials flow with the specified scopes.
/// This is a compatibility shim for the old GetAccessTokenAsync pattern.
/// </summary>
public static async Task<StellaOpsTokenResult> GetAccessTokenAsync(
this IStellaOpsTokenClient client,
IEnumerable<string> scopes,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(client);
var scope = scopes is not null ? string.Join(" ", scopes.Where(s => !string.IsNullOrWhiteSpace(s))) : null;
return await client.RequestClientCredentialsTokenAsync(scope, null, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Requests an access token using client credentials flow with a single scope.
/// </summary>
public static async Task<StellaOpsTokenResult> GetAccessTokenAsync(
this IStellaOpsTokenClient client,
string scope,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(client);
return await client.RequestClientCredentialsTokenAsync(scope, null, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Gets a cached access token or requests a new one if not cached or expired.
/// This is a compatibility shim for the old GetCachedAccessTokenAsync pattern.
/// </summary>
public static async Task<StellaOpsTokenCacheEntry> GetCachedAccessTokenAsync(
this IStellaOpsTokenClient client,
IEnumerable<string> scopes,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(client);
var scopeList = scopes?.Where(s => !string.IsNullOrWhiteSpace(s)).OrderBy(s => s).ToArray() ?? [];
var scope = string.Join(" ", scopeList);
var cacheKey = $"cc:{scope}";
// Check cache first
var cached = await client.GetCachedTokenAsync(cacheKey, cancellationToken).ConfigureAwait(false);
if (cached is not null && !cached.IsExpired(TimeProvider.System, TimeSpan.FromMinutes(1)))
{
return cached;
}
// Request new token
var result = await client.RequestClientCredentialsTokenAsync(scope, null, cancellationToken).ConfigureAwait(false);
var entry = result.ToCacheEntry();
// Cache the result
await client.CacheTokenAsync(cacheKey, entry, cancellationToken).ConfigureAwait(false);
return entry;
}
/// <summary>
/// Gets a cached access token or requests a new one if not cached or expired.
/// Single scope version.
/// </summary>
public static async Task<StellaOpsTokenCacheEntry> GetCachedAccessTokenAsync(
this IStellaOpsTokenClient client,
string scope,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(client);
var cacheKey = $"cc:{scope ?? "default"}";
// Check cache first
var cached = await client.GetCachedTokenAsync(cacheKey, cancellationToken).ConfigureAwait(false);
if (cached is not null && !cached.IsExpired(TimeProvider.System, TimeSpan.FromMinutes(1)))
{
return cached;
}
// Request new token
var result = await client.RequestClientCredentialsTokenAsync(scope, null, cancellationToken).ConfigureAwait(false);
var entry = result.ToCacheEntry();
// Cache the result
await client.CacheTokenAsync(cacheKey, entry, cancellationToken).ConfigureAwait(false);
return entry;
}
/// <summary>
/// Requests a token using client credentials. Parameterless version for simple cases.
/// </summary>
public static async Task<StellaOpsTokenResult> GetTokenAsync(
this IStellaOpsTokenClient client,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(client);
return await client.RequestClientCredentialsTokenAsync(null, null, cancellationToken).ConfigureAwait(false);
}
}