Skip to content
Open
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
38 changes: 38 additions & 0 deletions CosmosDBShell.Tests/CommandTests/ConnectCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace CosmosShell.Tests.CommandTests;
using Azure.Data.Cosmos.Shell.Core;
using Azure.Data.Cosmos.Shell.Lsp.Semantics;
using Azure.Data.Cosmos.Shell.Parser;
using Azure.Data.Cosmos.Shell.Util;
using Microsoft.Azure.Cosmos;

public class ConnectCommandTests
Expand Down Expand Up @@ -78,4 +79,41 @@ private static async Task<ConnectCommand> BindConnectCommandAsync(string command
var command = await statement.CreateCommandAsync(factory, shell, new CommandState(), CancellationToken.None);
return Assert.IsType<ConnectCommand>(command);
}

[Fact]
public void ConnectCommand_NotConnectedUsageHint_LocalizationKeysAreDefined()
{
// Issue #81: running `connect` while disconnected used to print only
// "Not connected" with no hint about how to authenticate. The hint
// strings must resolve to non-empty values.
Assert.False(string.IsNullOrWhiteSpace(MessageService.GetString("command-connect-not_connected-usage-header")));
Assert.False(string.IsNullOrWhiteSpace(MessageService.GetString("command-connect-not_connected-usage-footer")));
Assert.False(string.IsNullOrWhiteSpace(MessageService.GetString("shell-not_connected_hint")));
}

[Fact]
public void ConnectCommand_PrintConnectUsageHint_HasExamplesToPrint()
{
// The hint helper iterates the connect command's CosmosExample metadata and
// skips the bare `connect` no-arg form. Confirm there is at least one other
// example to display so the helper output is meaningful.
Assert.True(CommandFactory.TryCreateFactory(typeof(ConnectCommand), out var factory));

var examples = factory.ExamplesWithDescriptions
.Where(e => !string.IsNullOrWhiteSpace(e.Example) && e.Example != "connect")
.ToList();

Assert.NotEmpty(examples);
}

[Fact]
public void ConnectCommand_PrintConnectUsageHint_RunsWithoutThrowing()
{
using var shell = ShellInterpreter.CreateInstance();

// Smoke test: must not throw even when the shell's command map exposes the
// factory through ShellInterpreter.App.Commands.
var ex = Record.Exception(() => ConnectCommand.PrintConnectUsageHint(shell));
Assert.Null(ex);
}
}
42 changes: 42 additions & 0 deletions CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/ConnectCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,53 @@ internal static void AskForRBacPermissions(string principalId, string permission
ShellInterpreter.WriteLine(MessageService.GetArgsString("command-connect-rbac-error", "id", principalId, "permission", permission));
}

/// <summary>
/// Prints a short usage block with examples taken from the <see cref="CosmosExampleAttribute"/>
/// metadata on this command. Shown when the user runs <c>connect</c> without arguments while
/// disconnected so the available authentication options are discoverable without having to know
/// about <c>help connect</c> (see issue #81).
/// </summary>
internal static void PrintConnectUsageHint(ShellInterpreter shell)
{
AnsiConsole.MarkupLine(Markup.Escape(MessageService.GetString("command-connect-not_connected-usage-header")));

if (shell.App.Commands.TryGetValue("connect", out var factory))
{
const int MaxExamples = 2;
var shown = 0;
foreach (var (example, description) in factory.ExamplesWithDescriptions)
{
if (shown >= MaxExamples)
Comment thread
mkrueger marked this conversation as resolved.
{
break;
}

if (string.IsNullOrWhiteSpace(example) || example == "connect")
{
// Skip the no-arg example — that's the one the user just ran.
continue;
}

var highlighted = shell.BuildHighlightedMarkup(example);
AnsiConsole.MarkupLine($" {highlighted}");
if (!string.IsNullOrWhiteSpace(description))
{
AnsiConsole.MarkupLine($" [silver]{Markup.Escape(description)}[/]");
}

shown++;
}
}

AnsiConsole.MarkupLine(Markup.Escape(MessageService.GetString("command-connect-not_connected-usage-footer")));
}
Comment thread
mkrueger marked this conversation as resolved.

private static async Task<CommandState> PrintConnectionInfoAsync(ShellInterpreter shell, CommandState commandState, CancellationToken token)
{
if (shell.State is not ConnectedState connectedState)
{
AnsiConsole.MarkupLine(MessageService.GetString("command-connect-not_connected"));
PrintConnectUsageHint(shell);
commandState.IsPrinted = true;
var notConnectedJson = new Dictionary<string, object?>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,15 @@ internal async Task<int> RunAsync()
var result = 0;
this.PrintVersion(null);
WriteLine(MessageService.GetString("shell-ready"));

// First-run hint: if the shell starts without a connection, point users at
// the `connect` command. Otherwise users can land at the prompt with no
// obvious next step (see issue #81).
if (this.State is DisconnectedState)
{
AnsiConsole.MarkupLine("[yellow]" + Markup.Escape(MessageService.GetString("shell-not_connected_hint")) + "[/]");
}

while (this.IsRunning)
{
this.StdOutRedirect = null;
Expand Down
3 changes: 3 additions & 0 deletions CosmosDBShell/lang/en.ftl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
shell-ready = Cosmos DB shell ready.
shell-not_connected_hint = Not connected. Run 'connect <endpoint>' to authenticate, or 'help connect' for more options.
shell-hisory_file_deleted = History deleted.
shell-connect-browser-auth = Authenticating via browser. Please complete the login in the browser window that opens.
shell-connect-devicecode-auth = Browser authentication failed. Falling back to device code authentication.
Expand Down Expand Up @@ -324,6 +325,8 @@ command-connect-connected = Connected to account '{ $account }'
command-connect-emulator-detected = Emulator endpoint detected, using well-known account key and gateway mode.
command-connect-switching = Disconnecting from '{ $endpoint }'...
command-connect-not_connected = Not connected to any Cosmos DB account.
command-connect-not_connected-usage-header = Use 'connect <endpoint>' to authenticate. Common forms:
command-connect-not_connected-usage-footer = Run 'help connect' for the full list of options.
command-connect-info-title = Connection Information
command-connect-info-account = Account
command-connect-info-endpoint = Endpoint
Expand Down
Loading