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
Original file line number Diff line number Diff line change
Expand Up @@ -776,11 +776,14 @@ private static async Task ExecuteEndpointOnlyCleanupAsync(
return;
}

// Get the actual endpoint name that will be used for deletion (truncated to 42 chars)
var endpointName = EndpointHelper.GetEndpointName(config.BotName);

logger.LogInformation("");
logger.LogInformation("Endpoint Cleanup Preview:");
logger.LogInformation("============================");
logger.LogInformation("Will delete messaging endpoint:");
logger.LogInformation(" Endpoint Name: {BotName}", config.BotName);
logger.LogInformation(" Endpoint Name: {EndpointName}", endpointName);
logger.LogInformation(" Location: {Location}", config.Location);
logger.LogInformation("");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,38 @@
// Licensed under the MIT License.

using Microsoft.Agents.A365.DevTools.Cli.Constants;
using Microsoft.Agents.A365.DevTools.Cli.Exceptions;

namespace Microsoft.Agents.A365.DevTools.Cli.Services.Helpers;

public static class EndpointHelper
{
public static string GetEndpointName(string name)
{
return name.Length > 42
// Validate input
if (string.IsNullOrWhiteSpace(name))
{
throw new SetupValidationException("Endpoint name cannot be null or whitespace.");
}

// Truncate to 42 characters (Azure Bot Service maximum)
var truncated = name.Length > 42
? name.Substring(0, 42)
: name;

// Trim trailing hyphens to comply with Azure Bot Service naming rules
// Azure Bot Service does not allow bot names ending with hyphens
truncated = truncated.TrimEnd('-');

// Validate minimum length after trimming
if (truncated.Length < 4)
{
throw new SetupValidationException(
$"Endpoint name '{name}' becomes too short after processing (minimum 4 characters required). " +
"Please use a longer hostname or provide a custom endpoint name.");
}

return truncated;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using FluentAssertions;
using Microsoft.Agents.A365.DevTools.Cli.Exceptions;
using Microsoft.Agents.A365.DevTools.Cli.Services.Helpers;
using Xunit;

namespace Microsoft.Agents.A365.DevTools.Cli.Tests.Services.Helpers;

public class EndpointHelperTests
{
[Fact]
public void GetEndpointName_WhenNameIsUnder42Chars_ReturnsOriginalName()
{
// Arrange
var shortName = "my-endpoint";

// Act
var result = EndpointHelper.GetEndpointName(shortName);

// Assert
result.Should().Be("my-endpoint");
}

[Fact]
public void GetEndpointName_WhenNameIsExactly42Chars_ReturnsOriginalName()
{
// Arrange
var exactName = "twelve345678901234567890123456789012345678"; // 42 chars

// Act
var result = EndpointHelper.GetEndpointName(exactName);

// Assert
result.Should().Be(exactName);
result.Length.Should().Be(42);
}

[Fact]
public void GetEndpointName_WhenNameOver42Chars_TruncatesTo42Chars()
{
// Arrange
var longName = "this-is-a-very-long-endpoint-name-that-exceeds-the-limit-of-42-characters";

// Act
var result = EndpointHelper.GetEndpointName(longName);

// Assert
result.Length.Should().Be(42);
}

[Fact]
public void GetEndpointName_WhenTruncationEndsWithHyphen_ShouldTrimTrailingHyphen()
{
// Arrange - Simulates ngrok free domain scenario
// Original: distressingly-gnathonic-alonzo.ngrok-free.app
// After conversion: distressingly-gnathonic-alonzo-ngrok-free-app-endpoint
var longNameEndingWithHyphen = "distressingly-gnathonic-alonzo-ngrok-free-app-endpoint"; // 54 chars

// Act
var result = EndpointHelper.GetEndpointName(longNameEndingWithHyphen);

// Assert
result.Should().Be("distressingly-gnathonic-alonzo-ngrok-free", "should truncate to 42 chars and trim trailing hyphen");
result.Length.Should().Be(41);
result.Should().NotEndWith("-", "Azure Bot Service does not allow bot names ending with hyphen");
}

[Fact]
public void GetEndpointName_WhenTruncationEndsWithMultipleHyphens_ShouldTrimAllTrailingHyphens()
{
// Arrange - Edge case with multiple trailing hyphens after truncation
// Name that when truncated to 42 will end with "---e"
var nameWithMultipleHyphens = "some-very-long-endpoint-name-with-hyphens---extra-content-here"; // 63 chars

// Act
var result = EndpointHelper.GetEndpointName(nameWithMultipleHyphens);

// Assert - truncates to 42: "some-very-long-endpoint-name-with-hyphens-", then trims to "some-very-long-endpoint-name-with-hyphens"
result.Should().Be("some-very-long-endpoint-name-with-hyphens");
result.Length.Should().Be(41);
result.Should().NotEndWith("-", "Should trim all trailing hyphens");
}

[Theory]
[InlineData("my-endpoint-name-", "my-endpoint-name")]
[InlineData("endpoint--", "endpoint")]
[InlineData("test-name---", "test-name")]
public void GetEndpointName_WhenInputEndsWithHyphen_ShouldTrimTrailingHyphens(string input, string expected)
{
// Act
var result = EndpointHelper.GetEndpointName(input);

// Assert
result.Should().Be(expected);
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void GetEndpointName_WhenInputIsNullOrWhitespace_ShouldThrowSetupValidationException(string? input)
{
// Act
Action act = () => EndpointHelper.GetEndpointName(input!);

// Assert
act.Should().Throw<SetupValidationException>()
.WithMessage("*Endpoint name cannot be null or whitespace*");
}

[Fact]
public void GetEndpointName_WhenResultBecomesTooShort_ShouldThrowSetupValidationException()
{
// Arrange - Name that becomes less than 4 chars after trimming hyphens
var shortName = "---";

// Act
Action act = () => EndpointHelper.GetEndpointName(shortName);

// Assert
act.Should().Throw<SetupValidationException>()
.WithMessage("*becomes too short after processing*");
}
}
Loading