diff --git a/Directory.Packages.props b/Directory.Packages.props
index ef381efb..a4caa535 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -5,7 +5,7 @@
-
+
@@ -22,6 +22,7 @@
+
diff --git a/NGitLab.Tests/Docker/GitLabDockerContainer.cs b/NGitLab.Tests/Docker/GitLabDockerContainer.cs
index cd3a455f..775119c2 100644
--- a/NGitLab.Tests/Docker/GitLabDockerContainer.cs
+++ b/NGitLab.Tests/Docker/GitLabDockerContainer.cs
@@ -1,7 +1,4 @@
-#pragma warning disable MA0004
-#pragma warning disable MA0006
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
@@ -13,6 +10,9 @@
using System.Threading.Tasks;
using Docker.DotNet;
using Docker.DotNet.Models;
+using DotNet.Testcontainers.Builders;
+using DotNet.Testcontainers.Containers;
+using Microsoft.Extensions.Logging;
using NGitLab.Models;
using NUnit.Framework;
using Polly;
@@ -21,26 +21,33 @@ namespace NGitLab.Tests.Docker;
public class GitLabDockerContainer
{
- public const string ContainerName = "NGitLabClientTests";
+ public const string LocalContainerName = "NGitLabClientTests";
public const string ImageName = "gitlab/gitlab-ee";
///
- /// GitLab docker image version to spawn.
+ /// GitLab Docker image version to spawn.
/// Used only on local environment (CI should already have a running GitLab instance from its services)
///
///
/// Keep in sync with .github/workflows/ci.yml, use the lowest supported version
/// List of available versions: https://hub.docker.com/r/gitlab/gitlab-ee/tags/
///
- private const string LocalGitLabDockerVersion = "19.3.1-ee.0";
+ private const string LocalGitLabDockerVersion = "18.1.6-ee.0";
private static string s_creationErrorMessage;
private static readonly SemaphoreSlim s_setupLock = new(initialCount: 1, maxCount: 1);
private static GitLabDockerContainer s_instance;
- public string Host { get; private set; } = "localhost";
+ ///
+ /// Set only when the container was spawned locally via Testcontainers.
+ /// On CI, GitLab already runs as a pre-existing service container, so this stays null
+ /// and credential generation falls back to a raw Docker Engine API exec call.
+ ///
+ private IContainer _localContainer;
+
+ public string Host { get; } = "localhost";
- public int HttpPort { get; private set; } = 48624;
+ public int HttpPort { get; } = 48624;
public string AdminUserName { get; } = "root";
@@ -56,13 +63,11 @@ public static string AdminPassword
}
}
- public string LicenseFile { get; set; }
-
public Uri GitLabUrl => new("http://" + Host + ":" + HttpPort.ToString(CultureInfo.InvariantCulture));
public GitLabCredential Credentials { get; set; }
- public static async Task GetOrCreateInstance()
+ public static async Task GetOrCreateInstanceAsync()
{
await s_setupLock.WaitAsync().ConfigureAwait(false);
try
@@ -99,11 +104,11 @@ private async Task SetupAsync()
{
if (GitLabTestContext.IsContinuousIntegration())
{
- await WaitForCiGitLabInstance().ConfigureAwait(false);
+ await WaitForCiGitLabInstanceAsync().ConfigureAwait(false);
}
else
{
- await SpawnDockerContainerAsync().ConfigureAwait(false);
+ await SpawnLocalDockerContainerAsync().ConfigureAwait(false);
}
LoadCredentials();
@@ -115,10 +120,10 @@ private async Task SetupAsync()
}
await GenerateCredentialsAsync().ConfigureAwait(false);
- PersistCredentialsAsync();
+ PersistCredentials();
}
- private static async Task ValidateDockerIsEnabled(DockerClient client)
+ private static async Task ValidateCiDockerIsEnabled(DockerClient client)
{
try
{
@@ -135,151 +140,62 @@ private static async Task ValidateDockerIsEnabled(DockerClient client)
}
}
- private async Task SpawnDockerContainerAsync()
+ private async Task SpawnLocalDockerContainerAsync()
{
Console.WriteLine($"Executing tests locally. Spawning GitLab docker image version '{LocalGitLabDockerVersion}'");
- using var httpClient = new HttpClient();
- // Spawn the container
- // https://docs.gitlab.com/omnibus/settings/configuration.html
- using var conf = new DockerClientConfiguration(new Uri(OperatingSystem.IsWindows() ? "npipe://./pipe/docker_engine" : "unix:///var/run/docker.sock"));
- using var client = conf.CreateClient();
- await ValidateDockerIsEnabled(client);
-
- TestContext.Progress.WriteLine("Looking up GitLab Docker containers");
- var containers = await client.Containers.ListContainersAsync(new ContainersListParameters { All = true }).ConfigureAwait(false);
- var container = containers.FirstOrDefault(c => c.Names.Contains("/" + ContainerName, StringComparer.Ordinal));
- if (container != null)
- {
- TestContext.Progress.WriteLine("Verifying if the GitLab Docker container is using the right image");
- var inspect = await client.Containers.InspectContainerAsync(container.ID).ConfigureAwait(false);
- var inspectImage = await client.Images.InspectImageAsync(ImageName + ":" + LocalGitLabDockerVersion).ConfigureAwait(false);
- if (inspect.Image != inspectImage.ID)
- {
- TestContext.Progress.WriteLine("Ending GitLab Docker container, as it's using the wrong image");
- await client.Containers.RemoveContainerAsync(container.ID, new ContainerRemoveParameters { Force = true }).ConfigureAwait(false);
- container = null;
- }
- }
-
- if (container == null)
- {
- // Download GitLab images
- TestContext.Progress.WriteLine("Making sure the right GitLab Docker image is available locally");
- await client.Images.CreateImageAsync(new ImagesCreateParameters { FromImage = ImageName, Tag = LocalGitLabDockerVersion }, new AuthConfig(), new Progress()).ConfigureAwait(false);
-
- // Create the container
- TestContext.Progress.WriteLine("Creating the GitLab Docker container");
- var hostConfig = new HostConfig
- {
- PortBindings = new Dictionary>(StringComparer.Ordinal)
- {
- { HttpPort.ToString(CultureInfo.InvariantCulture) + "/tcp", new List { new PortBinding { HostPort = HttpPort.ToString(CultureInfo.InvariantCulture) } } },
- },
-
- // Update size of /dev/shm to to 512mb (default: 64mb)
- // Avoids intermittent crashes of GitLab
- ShmSize = 512 * 1024 * 1024,
- };
-
- // Disables non-useful features
- // See https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template
- string[] omnibusConfig =
- [
- $"external_url 'http://localhost:{HttpPort.ToString(CultureInfo.InvariantCulture)}/'",
- "gitlab_rails['gitlab_email_enabled'] = false",
- "gitlab_rails['incoming_email_enabled'] = false",
- "gitlab_rails['lfs_enabled'] = false",
- "gitlab_rails['terraform_state_enabled'] = false",
- "gitlab_rails['pages_object_store_enabled'] = false",
- "gitlab_rails['usage_ping_enabled'] = false",
- "gitlab_rails['registry_enabled'] = false",
- "registry['enable'] = false",
- "sidekiq['metrics_enabled'] = false",
- "logrotate['enable'] = false",
- "gitlab_pages['enable'] = false",
- "gitlab_rails['gitlab_kas_enabled'] = false",
- "mattermost['enable'] = false",
- "alertmanager['enable'] = false",
- "node_exporter['enable'] = false",
- "redis_exporter['enable'] = false",
- "postgres_exporter['enable'] = false",
- "pgbouncer_exporter['enable'] = false",
- "gitlab_exporter['enable'] = false",
- "gitlab_rails['kerberos_enabled'] = false",
- "gitlab_rails['packages_enabled'] = false",
- "gitlab_rails['dependency_proxy_enabled'] = false",
- ];
-
- var response = await client.Containers.CreateContainerAsync(new CreateContainerParameters
- {
- Hostname = "localhost",
- Image = ImageName + ":" + LocalGitLabDockerVersion,
- Name = ContainerName,
- Tty = false,
- HostConfig = hostConfig,
- ExposedPorts = new Dictionary(StringComparer.Ordinal)
- {
- { HttpPort.ToString(CultureInfo.InvariantCulture) + "/tcp", default },
- },
- Env =
- [
- $"GITLAB_ROOT_PASSWORD={AdminPassword}",
- $"GITLAB_OMNIBUS_CONFIG={string.Join("; ", omnibusConfig)}",
- ],
- }).ConfigureAwait(false);
-
- containers = await client.Containers.ListContainersAsync(new ContainersListParameters { All = true }).ConfigureAwait(false);
- container = containers.First(c => c.ID == response.ID);
- }
-
- // Start the container
- if (container.State != "running")
- {
- TestContext.Progress.WriteLine("Starting the GitLab Docker container");
- var started = await client.Containers.StartContainerAsync(container.ID, new ContainerStartParameters()).ConfigureAwait(false);
- if (!started)
- {
- Assert.Fail("Cannot start the Docker container");
- }
- }
-
- // Wait for the container to be ready.
- var stopwatch = Stopwatch.StartNew();
- while (true)
- {
- TestContext.Progress.WriteLine($@"Waiting for the GitLab Docker container to be ready ({stopwatch.Elapsed:mm\:ss})");
- var status = await client.Containers.InspectContainerAsync(container.ID);
- if (!status.State.Running)
- throw new InvalidOperationException($"Container '{status.ID}' is not running");
-
- var healthState = status.State.Health.Status;
-
- // unhealthy is valid as long as the container is running as it may indicate a slow creation
- if (healthState is "starting" or "unhealthy")
- {
- }
- else if (healthState is "healthy")
- {
- // A healthy container doesn't mean the service is actually running.
- // GitLab has lots of configuration steps that are still running when the container is healthy.
- try
- {
- using var response = await httpClient.GetAsync(GitLabUrl).ConfigureAwait(false);
- if (response.IsSuccessStatusCode)
- break;
- }
- catch
- {
- }
- }
- else
- {
- throw new InvalidOperationException($"Container status '{healthState}' is not supported");
- }
+ // Disables non-useful features
+ // See https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template
+ string[] omnibusConfig =
+ [
+ $"external_url 'http://localhost:{HttpPort.ToString(CultureInfo.InvariantCulture)}/'",
+ "gitlab_rails['gitlab_email_enabled'] = false",
+ "gitlab_rails['incoming_email_enabled'] = false",
+ "gitlab_rails['lfs_enabled'] = false",
+ "gitlab_rails['terraform_state_enabled'] = false",
+ "gitlab_rails['pages_object_store_enabled'] = false",
+ "gitlab_rails['usage_ping_enabled'] = false",
+ "gitlab_rails['registry_enabled'] = false",
+ "registry['enable'] = false",
+ "sidekiq['metrics_enabled'] = false",
+ "logrotate['enable'] = false",
+ "gitlab_pages['enable'] = false",
+ "gitlab_rails['gitlab_kas_enabled'] = false",
+ "mattermost['enable'] = false",
+ "alertmanager['enable'] = false",
+ "node_exporter['enable'] = false",
+ "redis_exporter['enable'] = false",
+ "postgres_exporter['enable'] = false",
+ "pgbouncer_exporter['enable'] = false",
+ "gitlab_exporter['enable'] = false",
+ "gitlab_rails['kerberos_enabled'] = false",
+ "gitlab_rails['packages_enabled'] = false",
+ "gitlab_rails['dependency_proxy_enabled'] = false",
+ ];
- await Task.Delay(5000);
- }
+ // https://docs.gitlab.com/omnibus/settings/configuration.html
+ // GitLab reports "healthy" long before it's actually ready to serve requests, so we wait
+ // on the HTTP endpoint itself rather than on the container's health status.
+ // WithReuse keeps an existing container running across local test runs (GitLab takes
+ // minutes to boot); bumping LocalGitLabDockerVersion requires removing the old container
+ // manually (`docker rm -f NGitLabClientTests`) since reuse matching is name+config based.
+ _localContainer = new ContainerBuilder(ImageName + ":" + LocalGitLabDockerVersion)
+ .WithName(LocalContainerName)
+ .WithHostname("localhost")
+ .WithPortBinding(HttpPort, HttpPort)
+ .WithEnvironment("GITLAB_ROOT_PASSWORD", AdminPassword)
+ .WithEnvironment("GITLAB_OMNIBUS_CONFIG", string.Join("; ", omnibusConfig))
+ .WithCreateParameterModifier(p => p.HostConfig.ShmSize = 512 * 1024 * 1024) // Default 64mb is too small and causes intermittent GitLab crashes
+ .WithWaitStrategy(Wait.ForUnixContainer()
+ .UntilHttpRequestIsSucceeded(
+ r => r.ForPort((ushort)HttpPort).ForPath("/"),
+ o => o.WithTimeout(TimeSpan.FromMinutes(10)).WithInterval(TimeSpan.FromSeconds(5))))
+ .WithReuse(true)
+ .WithLogger(TestProgressLogger.Instance)
+ .Build();
+
+ TestContext.Progress.WriteLine("Starting the GitLab Docker container (this can take several minutes on first run)");
+ await _localContainer.StartAsync().ConfigureAwait(false);
TestContext.Progress.WriteLine("GitLab Docker container is ready");
}
@@ -289,7 +205,7 @@ private async Task GenerateCredentialsAsync()
Console.WriteLine("Requesting credentials from GitLab instance");
var credentials = new GitLabCredential();
- await GenerateAdminToken(credentials).ConfigureAwait(false);
+ await GenerateAdminToken().ConfigureAwait(false);
if (credentials.AdminUserToken != null)
{
GenerateUserToken();
@@ -297,21 +213,16 @@ private async Task GenerateCredentialsAsync()
Credentials = credentials;
- async Task GenerateAdminToken(GitLabCredential credentials)
+ async Task GenerateAdminToken()
{
TestContext.Progress.WriteLine("Generating Credentials");
-
- using var conf = new DockerClientConfiguration(new Uri(OperatingSystem.IsWindows() ? "npipe://./pipe/docker_engine" : "unix:///var/run/docker.sock"));
- using var client = conf.CreateClient();
- await ValidateDockerIsEnabled(client).ConfigureAwait(false);
-
TestContext.Progress.WriteLine("Creating root token via 'gitlab-rails runner'");
// Keep only scopes the running GitLab version supports (an unknown scope makes `create!` raise).
- const string script = """
+ var script = $"""
desired_scopes = %w[api read_user read_api read_repository write_repository sudo admin_mode create_runner manage_runner k8s_proxy]
available_scopes = Gitlab::Auth.all_available_scopes.map(&:to_s)
- token = User.find_by_username!('root').personal_access_tokens.create!(
+ token = User.find_by_username!('{AdminUserName}').personal_access_tokens.create!(
name: 'NGitLabClientTest',
scopes: (desired_scopes & available_scopes),
expires_at: 1.year.from_now)
@@ -319,13 +230,20 @@ puts token.token
""";
var retryPolicy = Policy.Handle().WaitAndRetryAsync(20, _ => TimeSpan.FromSeconds(3));
- var token = await retryPolicy.ExecuteAsync(async () =>
+
+ if (_localContainer is not null)
{
- var containerId = await ResolveGitLabContainerIdAsync(client).ConfigureAwait(false);
- return await RunGitLabRailsRunnerAsync(client, containerId, script).ConfigureAwait(false);
- }).ConfigureAwait(false);
+ credentials.AdminUserToken = await retryPolicy.ExecuteAsync(() => RunGitLabRailsRunnerAsync(client: null, script)).ConfigureAwait(false);
+ }
+ else
+ {
+ using var client = new DockerClientBuilder()
+ .WithEndpoint(new Uri(OperatingSystem.IsWindows() ? "npipe://./pipe/docker_engine" : "unix:///var/run/docker.sock"))
+ .Build();
+ await ValidateCiDockerIsEnabled(client).ConfigureAwait(false);
- credentials.AdminUserToken = token;
+ credentials.AdminUserToken = await retryPolicy.ExecuteAsync(() => RunGitLabRailsRunnerAsync(client, script)).ConfigureAwait(false);
+ }
}
void GenerateUserToken()
@@ -361,7 +279,7 @@ void GenerateUserToken()
{
UserId = user.Id,
Name = "common_user",
- Scopes = new[] { "api" },
+ Scopes = ["api"],
ExpiresAt = DateTime.UtcNow.AddDays(7),
}));
@@ -369,12 +287,14 @@ void GenerateUserToken()
}
}
- private static async Task ResolveGitLabContainerIdAsync(DockerClient client)
+ private static async Task ResolveCiGitLabContainerIdAsync(DockerClient client)
{
var containers = await client.Containers.ListContainersAsync(new ContainersListParameters { All = true }).ConfigureAwait(false);
- var container = containers.FirstOrDefault(c => c.Names.Contains("/" + ContainerName, StringComparer.Ordinal))
- ?? containers.FirstOrDefault(c => c.Image.StartsWith(ImageName, StringComparison.Ordinal));
+ // On CI, GitLab runs as a pre-existing service container, which isn't named LocalContainerName
+ // (that name is only ever assigned by our own Testcontainers-managed local container), so
+ // we locate it by image instead.
+ var container = containers.FirstOrDefault(c => c.Image.StartsWith(ImageName, StringComparison.Ordinal));
if (container == null)
throw new InvalidOperationException($"Cannot find a running Docker container for image '{ImageName}' to generate credentials from.");
@@ -382,25 +302,41 @@ private static async Task ResolveGitLabContainerIdAsync(DockerClient cli
return container.ID;
}
- private static async Task RunGitLabRailsRunnerAsync(DockerClient client, string containerId, string script)
+ // When we spawned the container ourselves (local dev), Testcontainers already holds a reference to it
+ // and can exec into it directly. On CI, GitLab runs as a pre-existing service container we didn't create,
+ // so we fall back to the raw Docker Engine API to find it and exec into it.
+ private async Task RunGitLabRailsRunnerAsync(DockerClient client, string script)
{
- var execCreateResponse = await client.Exec.ExecCreateContainerAsync(containerId, new ContainerExecCreateParameters
- {
- AttachStdout = true,
- AttachStderr = true,
- Cmd = ["gitlab-rails", "runner", script],
- }).ConfigureAwait(false);
-
string stdout;
string stderr;
- using (var stream = await client.Exec.StartAndAttachContainerExecAsync(execCreateResponse.ID, tty: false).ConfigureAwait(false))
+ long? exitCode;
+
+ if (_localContainer is not null)
{
- (stdout, stderr) = await stream.ReadOutputToEndAsync(CancellationToken.None).ConfigureAwait(false);
+ var result = await _localContainer.ExecAsync(["gitlab-rails", "runner", script]).ConfigureAwait(false);
+ (stdout, stderr, exitCode) = (result.Stdout, result.Stderr, result.ExitCode);
}
+ else
+ {
+ var containerId = await ResolveCiGitLabContainerIdAsync(client).ConfigureAwait(false);
+ var execCreateResponse = await client.Exec.CreateContainerExecAsync(containerId, new ContainerExecCreateParameters
+ {
+ AttachStdout = true,
+ AttachStderr = true,
+ Cmd = ["gitlab-rails", "runner", script],
+ }).ConfigureAwait(false);
- var inspectResponse = await client.Exec.InspectContainerExecAsync(execCreateResponse.ID).ConfigureAwait(false);
- if (inspectResponse.ExitCode != 0)
- throw new InvalidOperationException($"'gitlab-rails runner' failed with exit code {inspectResponse.ExitCode}.\nStdout: {stdout}\nStderr: {stderr}");
+ using (var stream = await client.Exec.StartContainerExecAsync(execCreateResponse.ID, new ContainerExecStartParameters { TTY = false }).ConfigureAwait(false))
+ {
+ (stdout, stderr) = await stream.ReadOutputToEndAsync(CancellationToken.None).ConfigureAwait(false);
+ }
+
+ var inspectResponse = await client.Exec.InspectContainerExecAsync(execCreateResponse.ID).ConfigureAwait(false);
+ exitCode = inspectResponse.ExitCode;
+ }
+
+ if (exitCode != 0)
+ throw new InvalidOperationException($"'gitlab-rails runner' failed with exit code {exitCode}.\nStdout: {stdout}\nStderr: {stderr}");
var token = stdout
.Split('\n')
@@ -413,7 +349,7 @@ private static async Task RunGitLabRailsRunnerAsync(DockerClient client,
return token;
}
- private void PersistCredentialsAsync()
+ private void PersistCredentials()
{
var path = GetCredentialsFilePath();
Directory.CreateDirectory(Path.GetDirectoryName(path));
@@ -449,7 +385,7 @@ private static string GetCredentialsFilePath()
return Path.Combine(Path.GetTempPath(), "ngitlab", "credentials.json");
}
- private async Task WaitForCiGitLabInstance()
+ private async Task WaitForCiGitLabInstanceAsync()
{
Console.WriteLine($"Executing tests on CI. Checking GitLab instance...");
@@ -468,10 +404,39 @@ private async Task WaitForCiGitLabInstance()
{
}
- await Task.Delay(1000);
+ await Task.Delay(1000).ConfigureAwait(false);
}
s_creationErrorMessage = "GitLab is not well configured in CI";
Assert.Fail(s_creationErrorMessage);
}
+
+ // Testcontainers defaults to ConsoleLogger, but NUnit buffers Console output until the test
+ // finishes, so it doesn't give live feedback while the container is starting. Forwarding to
+ // TestContext.Progress instead surfaces Testcontainers' own lifecycle messages as they happen.
+ private sealed class TestProgressLogger : ILogger
+ {
+ public static readonly TestProgressLogger Instance = new();
+
+ public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Information;
+
+ public IDisposable BeginScope(TState state) => NullScope.Instance;
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
+ {
+ if (IsEnabled(logLevel))
+ {
+ TestContext.Progress.WriteLine(formatter(state, exception));
+ }
+ }
+
+ private sealed class NullScope : IDisposable
+ {
+ public static readonly NullScope Instance = new();
+
+ public void Dispose()
+ {
+ }
+ }
+ }
}
diff --git a/NGitLab.Tests/Docker/GitLabTestContext.cs b/NGitLab.Tests/Docker/GitLabTestContext.cs
index 7472a12a..f5209495 100644
--- a/NGitLab.Tests/Docker/GitLabTestContext.cs
+++ b/NGitLab.Tests/Docker/GitLabTestContext.cs
@@ -62,7 +62,7 @@ public static async Task CreateAsync()
// Disable proxy
Environment.SetEnvironmentVariable("http_proxy", "", EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("https_proxy", "", EnvironmentVariableTarget.Process);
- var container = await GitLabDockerContainer.GetOrCreateInstance().ConfigureAwait(false);
+ var container = await GitLabDockerContainer.GetOrCreateInstanceAsync().ConfigureAwait(false);
return new GitLabTestContext(container);
}
diff --git a/NGitLab.Tests/Docker/NGitLabRetryAttribute.cs b/NGitLab.Tests/Docker/NGitLabRetryAttribute.cs
index 34921c8a..31daeed3 100644
--- a/NGitLab.Tests/Docker/NGitLabRetryAttribute.cs
+++ b/NGitLab.Tests/Docker/NGitLabRetryAttribute.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal;
@@ -18,6 +19,11 @@ public TestCommand Wrap(TestCommand command)
public class RetryCommand : DelegatingTestCommand
{
+ // Some failures are caused by GitLab-side operations still settling asynchronously
+ // (e.g. a Sidekiq deletion job). Retrying instantly gives the server no time to catch up,
+ // so back off between attempts instead of hammering the same not-yet-resolved state.
+ private static readonly TimeSpan RetryDelay = TimeSpan.FromSeconds(3);
+
private readonly int _tryCount;
public RetryCommand(TestCommand innerCommand, int tryCount)
@@ -46,6 +52,7 @@ public override TestResult Execute(TestExecutionContext context)
if (count > 0)
{
+ Thread.Sleep(RetryDelay);
context.CurrentResult = context.CurrentTest.MakeTestResult();
context.CurrentRepeatCount++; // increment Retry count for next iteration. will only happen if we are guaranteed another iteration
}
diff --git a/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab.Tests/NGitLab.Tests.csproj
index 4e353f38..01e77c30 100644
--- a/NGitLab.Tests/NGitLab.Tests.csproj
+++ b/NGitLab.Tests/NGitLab.Tests.csproj
@@ -7,7 +7,7 @@
-
+
@@ -18,6 +18,7 @@
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/NGitLab.Tests/ProjectsTests.cs b/NGitLab.Tests/ProjectsTests.cs
index 70256e44..454858cc 100644
--- a/NGitLab.Tests/ProjectsTests.cs
+++ b/NGitLab.Tests/ProjectsTests.cs
@@ -355,7 +355,7 @@ public async Task CreateUpdateDelete(bool initiallySetTagsInsteadOfTopics)
var updatedProject2 = projectClient.Update(createdProject.PathWithNamespace, new ProjectUpdate { Visibility = VisibilityLevel.Internal });
Assert.That(updatedProject2.VisibilityLevel, Is.EqualTo(VisibilityLevel.Internal));
- projectClient.Delete(createdProject.Id);
+ await projectClient.PermanentlyDeleteAsync(createdProject.Id);
}
[Test]
@@ -701,8 +701,8 @@ public async Task ForkProject()
Assert.That(mr.AllowCollaboration, Is.True);
- projectClient.Delete(forkedProject.Id);
- projectClient.Delete(createdProject.Id);
+ await projectClient.PermanentlyDeleteAsync(forkedProject.Id);
+ await projectClient.PermanentlyDeleteAsync(createdProject.Id);
}
[Test]
@@ -810,7 +810,7 @@ public async Task CreateProjectWithSquashOption(SquashOption? inputSquashOption)
var expectedSquashOption = inputSquashOption ?? SquashOption.DefaultOff;
Assert.That(createdProject.SquashOption, Is.EqualTo(expectedSquashOption));
- projectClient.Delete(createdProject.Id);
+ await projectClient.PermanentlyDeleteAsync(createdProject.Id);
}
[Test]