Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ jobs:

integration:
runs-on: docs-builder-latest-16
environment: integration-tests
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6

Expand All @@ -195,5 +199,22 @@ jobs:
- name: Install Aspire workload
run: dotnet workload install aspire

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::197730964718:role/elastic-docs-v3-integration-tests
aws-region: us-east-1

- name: Fetch ES credentials
run: |
ES_URL=$(aws ssm get-parameter --name /elastic-docs-v3/prod/docs-elasticsearch-github-actions-readonly-url --with-decryption --query Parameter.Value --output text)
ES_KEY=$(aws ssm get-parameter --name /elastic-docs-v3/prod/docs-elasticsearch-github-actions-readonly-api-key --with-decryption --query Parameter.Value --output text)
echo "::add-mask::$ES_URL"
echo "::add-mask::$ES_KEY"
Comment on lines +212 to +213
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

dotnet user-secrets set "DocumentationElasticUrl" "$ES_URL" --project aspire
dotnet user-secrets set "DocumentationElasticApiKey" "$ES_KEY" --project aspire

- name: Integration Tests
env:
ENVIRONMENT: prod
run: dotnet run --project build -c release -- integrate
73 changes: 40 additions & 33 deletions aspire/AppHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,50 +54,57 @@ internal static async Task Run(
.WaitForCompletion(cloneAll)
.WithParentRelationship(cloneAll);

var elasticsearchLocal = builder.AddElasticsearch(ElasticsearchLocal)
.WithEnvironment("LICENSE", "trial");
if (!startElasticsearch)
elasticsearchLocal = elasticsearchLocal.WithExplicitStart();
IResourceBuilder<ElasticsearchResource>? elasticsearchLocal = null;
if (startElasticsearch)
elasticsearchLocal = builder.AddElasticsearch(ElasticsearchLocal)
.WithEnvironment("LICENSE", "trial");

var elasticsearchRemote = builder.AddExternalService(ElasticsearchRemote, elasticsearchUrl);

var api = builder.AddProject<Projects.Elastic_Documentation_Api>(Api)
// Read ENVIRONMENT and DOCS_BUILD_TYPE from the host process (injected by CI or set locally).
// Index name pattern: docs-{type}.semantic-{env}-latest
var rawEnvironment = Environment.GetEnvironmentVariable("ENVIRONMENT");
var serviceEnvironment = string.IsNullOrWhiteSpace(rawEnvironment) ? "prod" : rawEnvironment;
var rawBuildType = Environment.GetEnvironmentVariable("DOCS_BUILD_TYPE");
var buildType = string.IsNullOrWhiteSpace(rawBuildType) ? "assembler" : rawBuildType;

var api = builder.AddProject<Projects.Elastic_Documentation_Api>(Api, launchProfileName: "http")
.WithArgs(GlobalArguments)
.WithEnvironment("ENVIRONMENT", "dev")
.WithEnvironment("ENVIRONMENT", serviceEnvironment)
.WithEnvironment("DOCS_BUILD_TYPE", buildType)
.WithEnvironment("LLM_GATEWAY_FUNCTION_URL", llmUrl)
.WithEnvironment("LLM_GATEWAY_SERVICE_ACCOUNT_KEY_PATH", llmServiceAccountPath);
.WithEnvironment("LLM_GATEWAY_SERVICE_ACCOUNT_KEY_PATH", llmServiceAccountPath)
.WithHttpHealthCheck("/docs/_api/health");

// ReSharper disable once RedundantAssignment
api = startElasticsearch
? api
.WithReference(elasticsearchLocal)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchLocal.GetEndpoint("http"))
.WithEnvironment(context => context.EnvironmentVariables["DOCUMENTATION_ELASTIC_PASSWORD"] = elasticsearchLocal.Resource.PasswordParameter)
.WithParentRelationship(elasticsearchLocal)
.WaitFor(elasticsearchLocal)
.WithExplicitStart()
.WithReference(elasticsearchLocal!)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchLocal!.GetEndpoint("http"))
.WithEnvironment(context => context.EnvironmentVariables["DOCUMENTATION_ELASTIC_PASSWORD"] = elasticsearchLocal!.Resource.PasswordParameter)
.WithParentRelationship(elasticsearchLocal!)
.WaitFor(elasticsearchLocal!)
: api.WithReference(elasticsearchRemote)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchUrl)
.WithEnvironment("DOCUMENTATION_ELASTIC_APIKEY", elasticsearchApiKey)
.WithExplicitStart();
.WithEnvironment("DOCUMENTATION_ELASTIC_APIKEY", elasticsearchApiKey);

var mcp = builder.AddProject<Projects.Elastic_Documentation_Mcp_Remote>(RemoteMcp)
.WithArgs(GlobalArguments)
.WithEnvironment("ENVIRONMENT", "dev");
.WithEnvironment("ENVIRONMENT", serviceEnvironment)
.WithEnvironment("DOCS_BUILD_TYPE", buildType)
.WithHttpHealthCheck("/docs/_mcp/health");

// ReSharper disable once RedundantAssignment
mcp = startElasticsearch
? mcp
.WithReference(elasticsearchLocal)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchLocal.GetEndpoint("http"))
.WithEnvironment(context => context.EnvironmentVariables["DOCUMENTATION_ELASTIC_PASSWORD"] = elasticsearchLocal.Resource.PasswordParameter)
.WithParentRelationship(elasticsearchLocal)
.WaitFor(elasticsearchLocal)
.WithExplicitStart()
.WithReference(elasticsearchLocal!)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchLocal!.GetEndpoint("http"))
.WithEnvironment(context => context.EnvironmentVariables["DOCUMENTATION_ELASTIC_PASSWORD"] = elasticsearchLocal!.Resource.PasswordParameter)
.WithParentRelationship(elasticsearchLocal!)
.WaitFor(elasticsearchLocal!)
: mcp.WithReference(elasticsearchRemote)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchUrl)
.WithEnvironment("DOCUMENTATION_ELASTIC_APIKEY", elasticsearchApiKey)
.WithExplicitStart();
.WithEnvironment("DOCUMENTATION_ELASTIC_APIKEY", elasticsearchApiKey);

var indexElasticsearch = builder.AddProject<Projects.docs_builder>(ElasticsearchIngest)
.WithArgs(["assembler", "index", .. GlobalArguments])
Expand All @@ -107,11 +114,11 @@ internal static async Task Run(
// ReSharper disable once RedundantAssignment
indexElasticsearch = startElasticsearch
? indexElasticsearch
.WaitFor(elasticsearchLocal)
.WithReference(elasticsearchLocal)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchLocal.GetEndpoint("http"))
.WithEnvironment(context => context.EnvironmentVariables["DOCUMENTATION_ELASTIC_PASSWORD"] = elasticsearchLocal.Resource.PasswordParameter)
.WithParentRelationship(elasticsearchLocal)
.WaitFor(elasticsearchLocal!)
.WithReference(elasticsearchLocal!)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchLocal!.GetEndpoint("http"))
.WithEnvironment(context => context.EnvironmentVariables["DOCUMENTATION_ELASTIC_PASSWORD"] = elasticsearchLocal!.Resource.PasswordParameter)
.WithParentRelationship(elasticsearchLocal!)
: indexElasticsearch
.WithReference(elasticsearchRemote)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchUrl)
Expand All @@ -129,16 +136,16 @@ internal static async Task Run(

serveStatic = startElasticsearch
? serveStatic
.WithReference(elasticsearchLocal)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchLocal.GetEndpoint("http"))
.WithEnvironment(context => context.EnvironmentVariables["DOCUMENTATION_ELASTIC_PASSWORD"] = elasticsearchLocal.Resource.PasswordParameter)
.WithReference(elasticsearchLocal!)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchLocal!.GetEndpoint("http"))
.WithEnvironment(context => context.EnvironmentVariables["DOCUMENTATION_ELASTIC_PASSWORD"] = elasticsearchLocal!.Resource.PasswordParameter)
: serveStatic
.WithReference(elasticsearchRemote)
.WithEnvironment("DOCUMENTATION_ELASTIC_URL", elasticsearchUrl)
.WithEnvironment("DOCUMENTATION_ELASTIC_APIKEY", elasticsearchApiKey);

// ReSharper disable once RedundantAssignment
serveStatic = startElasticsearch ? serveStatic.WaitFor(elasticsearchLocal) : serveStatic.WaitFor(buildAll);
serveStatic = startElasticsearch ? serveStatic.WaitFor(elasticsearchLocal!) : serveStatic.WaitFor(buildAll);

await builder.Build().RunAsync(ct);
}
Expand Down
4 changes: 2 additions & 2 deletions docs-builder.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@
</Folder>
<Folder Name="/tests-integration/">
<File Path="tests-integration/Directory.Build.props" />
<Project Path="tests-integration/Elastic.Assembler.IntegrationTests/Elastic.Assembler.IntegrationTests.csproj" />
<Project Path="tests-integration/Elastic.Documentation.Api.IntegrationTests/Elastic.Documentation.Api.IntegrationTests.csproj" />
<Project Path="tests-integration/Elastic.Documentation.IntegrationTests/Elastic.Documentation.IntegrationTests.csproj" />
<Project Path="tests-integration/Search.IntegrationTests/Search.IntegrationTests.csproj" />
<Project Path="tests-integration/Mcp.Remote.IntegrationTests/Mcp.Remote.IntegrationTests.csproj" />
<Project Path="tests-integration/Elastic.ContentDateEnrichment.IntegrationTests/Elastic.ContentDateEnrichment.IntegrationTests.csproj" />
Expand All @@ -95,6 +94,7 @@
<Project Path="tests/authoring/authoring.fsproj" />
<Project Path="tests/Elastic.ApiExplorer.Tests/Elastic.ApiExplorer.Tests.csproj" />
<Project Path="tests/Elastic.Documentation.Api.Infrastructure.Tests/Elastic.Documentation.Api.Infrastructure.Tests.csproj" />
<Project Path="tests/Elastic.Documentation.Api.Tests/Elastic.Documentation.Api.Tests.csproj" />
<Project Path="tests/Elastic.Documentation.Build.Tests/Elastic.Documentation.Build.Tests.csproj" />
<Project Path="tests/Elastic.Documentation.Configuration.Tests/Elastic.Documentation.Configuration.Tests.csproj" />
<Project Path="tests/Elastic.Documentation.LegacyDocs.Tests/Elastic.Documentation.LegacyDocs.Tests.csproj" />
Expand Down
14 changes: 10 additions & 4 deletions src/api/Elastic.Documentation.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,17 @@
_ = builder.AddDefaultHealthChecks();
_ = builder.AddDocsApiOpenTelemetry();

// Configure Kestrel to listen on port 8080 (standard container port)
_ = builder.WebHost.ConfigureKestrel(serverOptions =>
// Only hardcode port 8080 when not running under Aspire/orchestration.
// Use builder.Configuration so both ASPNETCORE_* and DOTNET_* prefix variants are covered.
if (string.IsNullOrEmpty(builder.Configuration["HTTP_PORTS"])
&& string.IsNullOrEmpty(builder.Configuration["HTTPS_PORTS"])
&& string.IsNullOrEmpty(builder.Configuration["URLS"]))
Comment thread
coderabbitai[bot] marked this conversation as resolved.
{
serverOptions.ListenAnyIP(8080);
});
_ = builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(8080);
});
}

var environment = Environment.GetEnvironmentVariable("ENVIRONMENT");
Console.WriteLine($"Docs Environment: {environment}");
Expand Down
14 changes: 10 additions & 4 deletions src/api/Elastic.Documentation.Mcp.Remote/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,17 @@
_ = builder.Services.ConfigureOpenTelemetryTracerProvider(t =>
t.AddSource(McpToolTelemetry.McpToolSourceName));

// Configure Kestrel to listen on port 8080 (standard container port)
_ = builder.WebHost.ConfigureKestrel(serverOptions =>
// Only hardcode port 8080 when not running under Aspire/orchestration.
// Use builder.Configuration so both ASPNETCORE_* and DOTNET_* prefix variants are covered.
if (string.IsNullOrEmpty(builder.Configuration["HTTP_PORTS"])
&& string.IsNullOrEmpty(builder.Configuration["HTTPS_PORTS"])
&& string.IsNullOrEmpty(builder.Configuration["URLS"]))
{
serverOptions.ListenAnyIP(8080);
});
_ = builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(8080);
});
}

var environment = Environment.GetEnvironmentVariable("ENVIRONMENT");
Console.WriteLine($"Docs Environment: {environment}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
using Aspire.Hosting;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Testing;
using Elastic.Documentation.Aspire;
using Elastic.Documentation.ServiceDefaults;
using InMemLogger;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using static Elastic.Documentation.Aspire.ResourceNames;

[assembly: CaptureConsole, AssemblyFixture(typeof(Elastic.Assembler.IntegrationTests.DocumentationFixture))]
[assembly: CaptureConsole, AssemblyFixture(typeof(Elastic.Documentation.IntegrationTests.DocumentationFixture))]

namespace Elastic.Assembler.IntegrationTests;
namespace Elastic.Documentation.IntegrationTests;

public static class DistributedApplicationExtensions
{
Expand Down Expand Up @@ -90,6 +91,14 @@ public async ValueTask InitializeAsync()
_ = await DistributedApplication.ResourceNotifications
.WaitForResourceHealthyAsync(AssemblerServe, cancellationToken: TestContext.Current.CancellationToken)
.WaitAsync(TimeSpan.FromMinutes(3), TestContext.Current.CancellationToken);

_ = await DistributedApplication.ResourceNotifications
.WaitForResourceHealthyAsync(ResourceNames.Api, cancellationToken: TestContext.Current.CancellationToken)
.WaitAsync(TimeSpan.FromMinutes(3), TestContext.Current.CancellationToken);

_ = await DistributedApplication.ResourceNotifications
.WaitForResourceHealthyAsync(RemoteMcp, cancellationToken: TestContext.Current.CancellationToken)
.WaitAsync(TimeSpan.FromMinutes(3), TestContext.Current.CancellationToken);
}
catch (Exception e)
{
Expand All @@ -99,6 +108,10 @@ public async ValueTask InitializeAsync()
}
}

public HttpClient CreateApiClient() => DistributedApplication.CreateHttpClient(ResourceNames.Api);

public HttpClient CreateMcpClient() => DistributedApplication.CreateHttpClient(RemoteMcp);

private async ValueTask ValidateExitCode(string resourceName)
{
var eventResource = await DistributedApplication.ResourceNotifications.WaitForResourceAsync(resourceName, _ => true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using Microsoft.Extensions.Logging.Abstractions;
using Nullean.ScopedFileSystem;

namespace Elastic.Assembler.IntegrationTests;
namespace Elastic.Documentation.IntegrationTests;

public class PublicOnlyAssemblerConfigurationTests
{
Expand Down Expand Up @@ -130,7 +130,7 @@ public void ReadsVersions()
public ValueTask DisposeAsync()
{
GC.SuppressFinalize(this);
if (TestContext.Current.TestState?.Result is TestResult.Passed)
if (TestContext.Current.TestState?.Result is not TestResult.Failed)
return default;
foreach (var resource in _fixture.InMemoryLogger.RecordedLogs)
_output.WriteLine(resource.Message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
using OpenTelemetry;
using OpenTelemetry.Trace;

namespace Elastic.Assembler.IntegrationTests;
namespace Elastic.Documentation.IntegrationTests;

public class DocsSyncTests
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
<ProjectReference Include="$(SolutionRoot)\aspire\aspire.csproj" />
<ProjectReference Include="$(SolutionRoot)\src\Elastic.Documentation.ServiceDefaults\Elastic.Documentation.ServiceDefaults.csproj" />
<ProjectReference Include="$(SolutionRoot)\src\api\Elastic.Documentation.Api\Elastic.Documentation.Api.csproj" />
<ProjectReference Include="$(SolutionRoot)\src\api\Elastic.Documentation.Mcp.Remote\Elastic.Documentation.Mcp.Remote.csproj" />
<ProjectReference Include="$(SolutionRoot)\src\Elastic.Markdown\Elastic.Markdown.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FakeItEasy" />
<PackageReference Include="AngleSharp" />
<PackageReference Include="Aspire.Hosting.Testing"/>
<PackageReference Include="InMemoryLogger"/>
<PackageReference Include="ModelContextProtocol" />
<PackageReference Include="OpenTelemetry" />
<PackageReference Include="OpenTelemetry.Exporter.InMemory" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
using Nullean.ScopedFileSystem;
using RazorSlices;

namespace Elastic.Assembler.IntegrationTests;
namespace Elastic.Documentation.IntegrationTests;

public class NavigationBuildingTests(DocumentationFixture fixture, ITestOutputHelper output) : IAsyncLifetime
{
Expand Down Expand Up @@ -169,7 +169,7 @@ private static IEnumerable<string> GetAllNavigationUrls(INavigationItem item)
public ValueTask DisposeAsync()
{
GC.SuppressFinalize(this);
if (TestContext.Current.TestState?.Result is TestResult.Passed)
if (TestContext.Current.TestState?.Result is not TestResult.Failed)
return default;
foreach (var resource in fixture.InMemoryLogger.RecordedLogs)
output.WriteLine(resource.Message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
using Nullean.ScopedFileSystem;
using RazorSlices;

namespace Elastic.Assembler.IntegrationTests;
namespace Elastic.Documentation.IntegrationTests;

public class NavigationRootTests(DocumentationFixture fixture, ITestOutputHelper output) : IAsyncLifetime
{
Expand Down Expand Up @@ -78,7 +78,7 @@ public async Task AssertRealNavigation()
public ValueTask DisposeAsync()
{
GC.SuppressFinalize(this);
if (TestContext.Current.TestState?.Result is TestResult.Passed)
if (TestContext.Current.TestState?.Result is not TestResult.Failed)
return default;
foreach (var resource in fixture.InMemoryLogger.RecordedLogs)
output.WriteLine(resource.Message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Elastic.Assembler.IntegrationTests.Search;
namespace Elastic.Documentation.IntegrationTests.Search;

[CollectionDefinition(Collection)]
public class SearchBootstrapFixture(DocumentationFixture fixture) : IAsyncLifetime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using AwesomeAssertions;
using Elastic.Documentation.Search;

namespace Elastic.Assembler.IntegrationTests.Search;
namespace Elastic.Documentation.IntegrationTests.Search;

/// <summary>
/// Integration tests for the search endpoint exposed through MapSearchEndpoint.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

namespace Elastic.Assembler.IntegrationTests.Search;
namespace Elastic.Documentation.IntegrationTests.Search;

/// <summary>
/// Base class for search integration tests that handles initialization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using AwesomeAssertions;
using Elastic.Documentation.Aspire;

namespace Elastic.Assembler.IntegrationTests;
namespace Elastic.Documentation.IntegrationTests;

public class ServeStaticTests(DocumentationFixture fixture, ITestOutputHelper output) : IAsyncLifetime
{
Expand All @@ -23,7 +23,7 @@ public async Task AssertRequestToRootReturnsData()
public ValueTask DisposeAsync()
{
GC.SuppressFinalize(this);
if (TestContext.Current.TestState?.Result is TestResult.Passed)
if (TestContext.Current.TestState?.Result is not TestResult.Failed)
return default;
foreach (var resource in fixture.InMemoryLogger.RecordedLogs)
output.WriteLine(resource.Message);
Expand Down
Loading
Loading