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
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:
32
src/Cli/StellaOps.Cli/Extensions/CommandLineExtensions.cs
Normal file
32
src/Cli/StellaOps.Cli/Extensions/CommandLineExtensions.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.CommandLine;
|
||||
|
||||
namespace StellaOps.Cli.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Compatibility extensions for System.CommandLine 2.0.0-beta5+ API changes.
|
||||
/// These restore the older extension method patterns that were used in earlier versions.
|
||||
/// See: https://learn.microsoft.com/en-us/dotnet/standard/commandline/migration-guide-2.0.0-beta5
|
||||
/// </summary>
|
||||
public static class CommandLineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the default value for an option (compatibility shim for older API).
|
||||
/// In beta5+, this maps to DefaultValueFactory.
|
||||
/// </summary>
|
||||
public static Option<T> SetDefaultValue<T>(this Option<T> option, T defaultValue)
|
||||
{
|
||||
option.DefaultValueFactory = _ => defaultValue;
|
||||
return option;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restricts the option to accept only the specified values (compatibility shim).
|
||||
/// Works for both Option<string> and Option<string?>.
|
||||
/// </summary>
|
||||
public static Option<T> FromAmong<T>(this Option<T> option, params string[] allowedValues)
|
||||
where T : class?
|
||||
{
|
||||
option.AcceptOnlyFromAmong(allowedValues);
|
||||
return option;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user