Skip to content

Smoketests can run against remote servers #3012

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d40b7d9
[release/v1.3.0]: empty commit to allow PR creation
bfops Jul 31, 2025
8d30ed7
[bfops/release-smoketests]: [WIP] update smoketests to be able to run…
bfops Jul 31, 2025
e8b675a
[bfops/release-smoketests]: https
bfops Jul 31, 2025
fdea829
[bfops/release-smoketests]: config changes
bfops Aug 4, 2025
33c6aec
[bfops/release-smoketests]: TODO
bfops Aug 4, 2025
8d878fe
[bfops/release-smoketests]: Merge remote-tracking branch 'origin/mast…
bfops Aug 8, 2025
3be768a
[bfops/release-smoketests]: handle http/https gracefully
bfops Aug 11, 2025
76682ed
[bfops/release-smoketests]: defualt_server
bfops Aug 11, 2025
bb36753
[bfops/release-smoketests]: fix a bunch of tests
bfops Aug 11, 2025
ee88748
[bfops/release-smoketests]: fix api_call
bfops Aug 11, 2025
8d7596c
[bfops/release-smoketests]: change
bfops Aug 11, 2025
2e8dff7
[bfops/release-smoketests]: more work
bfops Aug 11, 2025
94abcae
[bfops/release-smoketests]: fix
bfops Aug 11, 2025
52c87f0
[bfops/release-smoketests]: fix hang
bfops Aug 11, 2025
06beeb4
[bfops/release-smoketests]: tweak
bfops Aug 11, 2025
891825b
[bfops/release-smoketests]: move comment
bfops Aug 11, 2025
57a6762
[bfops/release-smoketests]: whitespace
bfops Aug 11, 2025
548c075
[bfops/release-smoketests]: review
bfops Aug 11, 2025
9d86c52
[bfops/release-smoketests]: review
bfops Aug 11, 2025
ef755aa
[bfops/release-smoketests]: comments
bfops Aug 11, 2025
f7eb981
[bfops/release-smoketests]: fix
bfops Aug 11, 2025
cd2aa0b
[bfops/release-smoketests]: review
bfops Aug 12, 2025
3b5362c
[bfops/release-smoketests]: fix
bfops Aug 12, 2025
f3d292a
[bfops/release-smoketests]: fix
bfops Aug 12, 2025
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
32 changes: 30 additions & 2 deletions smoketests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
# and a dotnet installation is detected
HAVE_DOTNET = False

# When we pass --spacetime-login, we are running against a server that requires "real" spacetime logins (rather than `--server-issued-login`).
# This is used to skip tests that don't work with that.
USE_SPACETIME_LOGIN = False

# If we pass `--remote-server`, the server address will be something other than the default. This is used to skip tests that rely on use
# having the default localhost server.
REMOTE_SERVER = False

# default value can be overridden by `--compose-file` flag
COMPOSE_FILE = "./docker-compose.yml"

Expand All @@ -61,6 +69,15 @@ def requires_dotnet(item):
return item
return unittest.skip("dotnet 8.0 not available")(item)

def requires_anonymous_login(item):
if USE_SPACETIME_LOGIN:
return unittest.skip("using `spacetime login`")(item)
return item

def requires_local_server(item):
if REMOTE_SERVER:
return unittest.skip("running against a remote server")(item)
return item

def build_template_target():
if not TEMPLATE_TARGET_DIR.exists():
Expand Down Expand Up @@ -260,9 +277,20 @@ def run():
def api_call(self, method, path, body = None, headers = {}):
with open(self.config_path, "rb") as f:
config = tomllib.load(f)
host = config['default_server']
token = config['spacetimedb_token']
conn = http.client.HTTPConnection(host)
server_name = config['default_server']
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this corresponds to the change to the config.toml below; now the default_server is expected to be a server nickname instead of a hostname. This allows us to 1) avoid duplicating the hostname whenever we want to update it, and 2) look up the protocol associated with this hostname (which we previously didn't have a way of providing).

server_config = next((c for c in config['server_configs'] if c['nickname'] == server_name), None)
if server_config is None:
raise Exception(f"Unable to find server in config with nickname {server_name}")
host = server_config['host']
protocol = server_config['protocol']
conn = None
if protocol == "http":
conn = http.client.HTTPConnection(host)
elif protocol == "https":
conn = http.client.HTTPSConnection(host)
else:
raise Exception(f"Unknown protocol: {protocol}")
auth = {"Authorization": f'Bearer {token}'}
headers.update(auth)
log_cmd([method, path])
Expand Down
13 changes: 12 additions & 1 deletion smoketests/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ def main():
parser.add_argument("-x", dest="exclude", nargs="*", default=[])
parser.add_argument("--no-build-cli", action="store_true", help="don't cargo build the cli")
parser.add_argument("--list", action="store_true", help="list the tests that would be run, but don't run them")
parser.add_argument("--remote-server", action="store", help="Run against a remote server")
parser.add_argument("--spacetime-login", action="store_true", help="Use `spacetime login` for these tests (and disable tests that don't work with that)")
args = parser.parse_args()

if not args.no_build_cli:
Expand Down Expand Up @@ -110,7 +112,16 @@ def main():
subprocess.Popen(["docker", "logs", "-f", docker_container])
smoketests.HAVE_DOCKER = True

smoketests.new_identity(TEST_DIR / 'config.toml')
if args.remote_server is not None:
smoketests.spacetime("--config-path", TEST_DIR / 'config.toml', "server", "edit", "localhost", "--url", args.remote_server, "--yes")
smoketests.REMOTE_SERVER = True

if args.spacetime_login:
smoketests.spacetime("--config-path", TEST_DIR / 'config.toml', "logout")
smoketests.spacetime("--config-path", TEST_DIR / 'config.toml', "login")
smoketests.USE_SPACETIME_LOGIN = True
else:
smoketests.new_identity(TEST_DIR / 'config.toml')

if not args.skip_dotnet:
smoketests.HAVE_DOTNET = check_dotnet()
Expand Down
2 changes: 1 addition & 1 deletion smoketests/config.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
default_server = "127.0.0.1:3000"
default_server = "localhost"
spacetimedb_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJoZXhfaWRlbnRpdHkiOiJjMjAwYzc3NDY1NTE5MDM2MTE4M2JiNjFmMWMxYzY3NDUzMzYzY2MxMTY4MmM1NTUwNWZiNjdlYzI0ZWMyMWViIiwic3ViIjoiOTJlMmNkOGQtNTk5Ny00NjZlLWIwNmYtZDNjOGQ1NzU3ODI4IiwiaXNzIjoibG9jYWxob3N0IiwiYXVkIjpbInNwYWNldGltZWRiIl0sImlhdCI6MTc1MjA0NjgwMCwiZXhwIjpudWxsfQ.dgefoxC7eCOONVUufu2JTVFo9876zQ4Mqwm0ivZ0PQK7Hacm3Ip_xqyav4bilZ0vIEf8IM8AB0_xawk8WcbvMg"
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

instead of using a hardcoded address, use a nickname for the server specified below. This allows us to look up the correct protocol for that server.


[[server_configs]]
Expand Down
3 changes: 2 additions & 1 deletion smoketests/tests/energy.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from .. import Smoketest
from .. import Smoketest, requires_anonymous_login
import time

class EnergyFlow(Smoketest):

@requires_anonymous_login
def test_energy_balance(self):
"""Test getting energy balance."""

Expand Down
3 changes: 2 additions & 1 deletion smoketests/tests/new_user_flow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .. import Smoketest
from .. import Smoketest, requires_anonymous_login
import time

class NewUserFlow(Smoketest):
Expand All @@ -25,6 +25,7 @@ class NewUserFlow(Smoketest):
}
"""

@requires_anonymous_login
def test_new_user_flow(self):
"""Test the entirety of the new user flow."""

Expand Down
18 changes: 4 additions & 14 deletions smoketests/tests/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ def setUp(self):
def test_call(self):
"""Ensure that anyone has the permission to call any standard reducer"""

self.new_identity()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't believe this new_identity really added anything to the test, but it hamstrung the test when running against servers that required a spacetime login.


self.publish_module()

self.call("say_hello", anon=True)
Expand All @@ -21,28 +19,22 @@ def test_call(self):
def test_delete(self):
"""Ensure that you cannot delete a database that you do not own"""

self.new_identity()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't believe this new_identity really added anything to the test, but it hamstrung the test when running against servers that required a spacetime login.


self.publish_module()

self.reset_config()
self.new_identity()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the goal of the reset_config was to be using a different identity than the publish. This accomplishes that directly.

with self.assertRaises(Exception):
self.spacetime("delete", self.database_identity)

def test_describe(self):
"""Ensure that anyone can describe any database"""

self.new_identity()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't believe this new_identity really added anything to the test, but it hamstrung the test when running against servers that required a spacetime login.

self.publish_module()

self.reset_config()
self.new_identity()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed these in favor of just using --anonymous

self.spacetime("describe", "--json", self.database_identity)
self.spacetime("describe", "--anonymous", "--json", self.database_identity)

def test_logs(self):
"""Ensure that we are not able to view the logs of a module that we don't have permission to view"""

self.new_identity()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't believe this new_identity really added anything to the test (because we just use new_identity to change identities below anyway), but it hamstrung the test when running against servers that required a spacetime login to publish.

self.publish_module()

self.reset_config()
Expand All @@ -57,10 +49,9 @@ def test_logs(self):
def test_publish(self):
"""This test checks to make sure that you cannot publish to an identity that you do not own."""

self.new_identity()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't believe this new_identity really added anything to the test, but it hamstrung the test when running against servers that required a spacetime login.

self.publish_module()

self.reset_config()
self.new_identity()
Copy link
Collaborator Author

@bfops bfops Aug 12, 2025

Choose a reason for hiding this comment

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

the goal of this reset_config was effectively to switch identities, which we do directly now


with self.assertRaises(Exception):
# TODO: This raises for the wrong reason - `--clear-database` doesn't exist anymore!
Expand All @@ -73,11 +64,10 @@ def test_publish(self):
def test_replace_names(self):
"""Test that you can't replace names of a database you don't own"""

self.new_identity()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't believe this new_identity really added anything to the test, but it hamstrung the test when running against servers that required a spacetime login.

name = random_string()
self.publish_module(name)

self.reset_config()
self.new_identity()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the goal of this reset_config was effectively to switch identities, which we do directly now


with self.assertRaises(Exception):
self.api_call(
Expand Down
1 change: 1 addition & 0 deletions smoketests/tests/replication.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ def tearDown(self):
self.docker.compose("up", "-d")
super().tearDown()

# TODO: This function seems to run even when `--docker` is not passed, leading to errors unless `-x replication` is passed, due to the docker-related code below.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

pretty unrelated, but I ran into this while testing

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

Expand Down
4 changes: 3 additions & 1 deletion smoketests/tests/servers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from .. import Smoketest, extract_field
from .. import Smoketest, extract_field, requires_local_server
import re

# We require a local server because these tests have hardcoded server addresses.
@requires_local_server
class Servers(Smoketest):
AUTOPUBLISH = False

Expand Down
Loading