diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 01dd6a7c..1b0937f2 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ?? +### Fixed + +- `rsconnect list` now properly functions when a stored server has no nickname. + ## [1.28.0] - 2025-11-06 ### Added diff --git a/rsconnect/metadata.py b/rsconnect/metadata.py index 6e731b56..7ea6e180 100644 --- a/rsconnect/metadata.py +++ b/rsconnect/metadata.py @@ -15,7 +15,16 @@ from io import BufferedWriter from os.path import abspath, basename, dirname, exists, join from threading import Lock -from typing import TYPE_CHECKING, Callable, Dict, Generic, Mapping, Optional, TypeVar, Union +from typing import ( + TYPE_CHECKING, + Callable, + Dict, + Generic, + Mapping, + Optional, + TypeVar, + Union, +) from urllib.parse import urlparse # Even though TypedDict is available in Python 3.8, because it's used with NotRequired, @@ -316,7 +325,7 @@ def get_all_servers(self): :return: the sorted list of known servers. """ - return self._get_sorted_values(lambda s: s["name"]) + return self._get_sorted_values(lambda s: s.get("name") or "") def set( self, diff --git a/tests/test_main.py b/tests/test_main.py index 01b6ecbc..a4692a74 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -4,7 +4,6 @@ from os.path import join from unittest import TestCase, mock - import click import httpretty import pytest diff --git a/tests/test_metadata.py b/tests/test_metadata.py index f64882cf..c144626f 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -32,7 +32,8 @@ def setUp(self): secret="c29tZVNlY3JldAo=", ) self.server_store.set("qux", "https://example.snowflakecomputing.app", snowflake_connection_name="dev") - self.assertEqual(len(self.server_store.get_all_servers()), 4, "Unexpected servers after setup") + self.server_store.set("None", "http://connect.test", "notAnApiKey") + self.assertEqual(len(self.server_store.get_all_servers()), 5, "Unexpected servers after setup") def tearDown(self): # clean up our temp test directory created with tempfile.mkdtemp() @@ -93,21 +94,23 @@ def test_remove_by_url(self): def test_remove_not_found(self): self.assertFalse(self.server_store.remove_by_name("frazzle")) - self.assertEqual(len(self.server_store.get_all_servers()), 4) + self.assertEqual(len(self.server_store.get_all_servers()), 5) self.assertFalse(self.server_store.remove_by_url("http://frazzle")) - self.assertEqual(len(self.server_store.get_all_servers()), 4) + self.assertEqual(len(self.server_store.get_all_servers()), 5) def test_list(self): servers = self.server_store.get_all_servers() - self.assertEqual(len(servers), 4) - self.assertEqual(servers[0]["name"], "bar") - self.assertEqual(servers[0]["url"], "http://connect.remote") - self.assertEqual(servers[1]["name"], "baz") - self.assertEqual(servers[1]["url"], "https://shinyapps.io") - self.assertEqual(servers[2]["name"], "foo") - self.assertEqual(servers[2]["url"], "http://connect.local") - self.assertEqual(servers[3]["name"], "qux") - self.assertEqual(servers[3]["url"], "https://example.snowflakecomputing.app") + self.assertEqual(len(servers), 5) + self.assertEqual(servers[0]["name"], "None") + self.assertEqual(servers[0]["url"], "http://connect.test") + self.assertEqual(servers[1]["name"], "bar") + self.assertEqual(servers[1]["url"], "http://connect.remote") + self.assertEqual(servers[2]["name"], "baz") + self.assertEqual(servers[2]["url"], "https://shinyapps.io") + self.assertEqual(servers[3]["name"], "foo") + self.assertEqual(servers[3]["url"], "http://connect.local") + self.assertEqual(servers[4]["name"], "qux") + self.assertEqual(servers[4]["url"], "https://example.snowflakecomputing.app") def check_resolve_call(self, name, server, api_key, insecure, ca_cert, should_be_from_store): server_data = self.server_store.resolve(name, server) @@ -133,6 +136,7 @@ def test_resolve_by_default(self): self.server_store.remove_by_url("http://connect.remote") self.server_store.remove_by_url("https://shinyapps.io") self.server_store.remove_by_name("qux") + self.server_store.remove_by_name("None") self.check_resolve_call(None, None, None, None, None, True) def test_resolve_from_args(self):