Skip to content

Commit f3341e8

Browse files
committed
[bfops/bump-versions]: Merge remote-tracking branch 'origin/bfops/fix-nuget-smoketests' into bfops/bump-versions
2 parents 49b780a + 947375f commit f3341e8

File tree

4 files changed

+87
-11
lines changed

4 files changed

+87
-11
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ jobs:
5050
- uses: actions/setup-dotnet@v4
5151
with:
5252
global-json-file: global.json
53+
54+
# nodejs and pnpm are required for the typescript quickstart smoketest
55+
- name: Set up Node.js
56+
uses: actions/setup-node@v4
57+
with:
58+
node-version: 18
59+
60+
- uses: pnpm/action-setup@v4
61+
with:
62+
run_install: true
63+
5364
- name: Install psql (Windows)
5465
if: runner.os == 'Windows'
5566
run: choco install psql -y --no-progress

smoketests/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import logging
1515
import http.client
1616
import tomllib
17+
import functools
1718

1819
# miscellaneous file paths
1920
TEST_DIR = Path(__file__).parent
@@ -28,6 +29,7 @@
2829
TEMPLATE_CARGO_TOML = open(STDB_DIR / "crates/cli/templates/basic-rust/server/Cargo.toml").read()
2930
bindings_path = (STDB_DIR / "crates/bindings").absolute()
3031
escaped_bindings_path = str(bindings_path).replace('\\', '\\\\\\\\') # double escape for re.sub + toml
32+
TYPESCRIPT_BINDINGS_PATH = (STDB_DIR / "crates/bindings-typescript").absolute()
3133
TEMPLATE_CARGO_TOML = (re.compile(r"^spacetimedb\s*=.*$", re.M) \
3234
.sub(f'spacetimedb = {{ path = "{escaped_bindings_path}", features = {{features}} }}', TEMPLATE_CARGO_TOML))
3335

@@ -170,6 +172,21 @@ def run_cmd(*args, capture_stderr=True, check=True, full_output=False, cmd_name=
170172
output.check_returncode()
171173
return output if full_output else output.stdout
172174

175+
@functools.cache
176+
def pnpm_path():
177+
pnpm = shutil.which("pnpm")
178+
if not pnpm:
179+
raise Exception("pnpm not installed")
180+
return pnpm
181+
182+
def pnpm(*args, **kwargs):
183+
return run_cmd(pnpm_path(), *args, **kwargs)
184+
185+
@functools.cache
186+
def build_typescript_sdk():
187+
pnpm("install", cwd=TYPESCRIPT_BINDINGS_PATH)
188+
pnpm("build", cwd=TYPESCRIPT_BINDINGS_PATH)
189+
173190
def spacetime(*args, **kwargs):
174191
return run_cmd(SPACETIME_BIN, *args, cmd_name="spacetime", **kwargs)
175192

smoketests/docker.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,21 @@ def restart_docker():
2626

2727
# Ensure all nodes are running.
2828
attempts = 0
29-
while attempts < 5:
29+
while attempts < 10:
3030
attempts += 1
31-
if all(container.is_running(docker, spacetimedb_ping_url) for container in containers):
31+
containers_alive = {
32+
container.name: container.is_running(docker, spacetimedb_ping_url)
33+
for container in containers
34+
}
35+
if all(containers_alive.values()):
3236
# sleep a bit more to allow for leader election etc
3337
# TODO: make ping endpoint consider all server state
3438
time.sleep(2)
3539
return
3640
else:
3741
time.sleep(1)
3842

39-
raise Exception("Not all containers are up and running")
43+
raise Exception(f"Not all containers are up and running: {containers_alive!r}")
4044

4145
def spacetimedb_ping_url(port: int) -> str:
4246
return f"http://127.0.0.1:{port}/v1/ping"

smoketests/tests/quickstart.py

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import xmltodict
77

88
import smoketests
9-
from .. import Smoketest, STDB_DIR, run_cmd, TEMPLATE_CARGO_TOML
9+
from .. import Smoketest, STDB_DIR, run_cmd, TEMPLATE_CARGO_TOML, TYPESCRIPT_BINDINGS_PATH, build_typescript_sdk, pnpm
1010

1111

1212
def _write_file(path: Path, content: str):
@@ -19,12 +19,13 @@ def _append_to_file(path: Path, content: str):
1919
f.write(content)
2020

2121

22-
def _parse_quickstart(doc_path: Path, language: str) -> str:
22+
def _parse_quickstart(doc_path: Path, language: str, module_name: str) -> str:
2323
"""Extract code blocks from `quickstart.md` docs.
2424
This will replicate the steps in the quickstart guide, so if it fails the quickstart guide is broken.
2525
"""
2626
content = Path(doc_path).read_text()
27-
blocks = re.findall(rf"```{language}\n(.*?)\n```", content, re.DOTALL)
27+
codeblock_lang = "ts" if language == "typescript" else language
28+
blocks = re.findall(rf"```{codeblock_lang}\n(.*?)\n```", content, re.DOTALL)
2829

2930
end = ""
3031
if language == "csharp":
@@ -42,7 +43,7 @@ def _parse_quickstart(doc_path: Path, language: str) -> str:
4243
filtered_blocks.append(block)
4344
blocks = filtered_blocks
4445
# So we could have a different db for each language
45-
return "\n".join(blocks).replace("quickstart-chat", f"quickstart-chat-{language}") + end
46+
return "\n".join(blocks).replace("quickstart-chat", module_name) + end
4647

4748
def load_nuget_config(p: Path):
4849
if p.exists():
@@ -101,6 +102,8 @@ class BaseQuickstart(Smoketest):
101102
MODULE_CODE = ""
102103

103104
lang = None
105+
client_lang = None
106+
codeblock_langs = None
104107
server_doc = None
105108
client_doc = None
106109
server_file = None
@@ -118,12 +121,16 @@ def project_init(self, path: Path):
118121
def sdk_setup(self, path: Path):
119122
raise NotImplementedError
120123

124+
@property
125+
def _module_name(self):
126+
return f"quickstart-chat-{self.lang}"
127+
121128
def _publish(self) -> Path:
122129
base_path = Path(self.enterClassContext(tempfile.TemporaryDirectory()))
123130
server_path = base_path / "server"
124131

125132
self.generate_server(server_path)
126-
self.publish_module(f"quickstart-chat-{self.lang}", capture_stderr=True, clear=True)
133+
self.publish_module(self._module_name, capture_stderr=True, clear=True)
127134
return base_path / "client"
128135

129136
def generate_server(self, server_path: Path):
@@ -141,7 +148,7 @@ def generate_server(self, server_path: Path):
141148
)
142149
self.project_path = server_path / "spacetimedb"
143150
shutil.copy2(STDB_DIR / "rust-toolchain.toml", self.project_path)
144-
_write_file(self.project_path / self.server_file, _parse_quickstart(self.server_doc, self.lang))
151+
_write_file(self.project_path / self.server_file, _parse_quickstart(self.server_doc, self.lang, self._module_name))
145152
self.server_postprocess(self.project_path)
146153
self.spacetime("build", "-d", "-p", self.project_path, capture_stderr=True)
147154

@@ -163,13 +170,14 @@ def _test_quickstart(self):
163170

164171
run_cmd(*self.build_cmd, cwd=client_path, capture_stderr=True)
165172

173+
client_lang = self.client_lang or self.lang
166174
self.spacetime(
167-
"generate", "--lang", self.lang,
175+
"generate", "--lang", client_lang,
168176
"--out-dir", client_path / self.module_bindings,
169177
"--project-path", self.project_path, capture_stderr=True
170178
)
171179
# Replay the quickstart guide steps
172-
main = _parse_quickstart(self.client_doc, self.lang)
180+
main = _parse_quickstart(self.client_doc, client_lang, self._module_name)
173181
for src, dst in self.replacements.items():
174182
main = main.replace(src, dst)
175183
main += "\n" + self.extra_code
@@ -301,6 +309,19 @@ def sdk_setup(self, path: Path):
301309
source_dir=(STDB_DIR / "crates/bindings-csharp/BSATN.Runtime").absolute(),
302310
build_subdir="bin/Release"
303311
)
312+
# This one is only needed because the regression-tests subdir uses it
313+
override_nuget_package(
314+
project_dir=STDB_DIR/"sdks/csharp",
315+
package="SpacetimeDB.Runtime",
316+
source_dir=(STDB_DIR / "crates/bindings-csharp/Runtime").absolute(),
317+
build_subdir="bin/Release"
318+
)
319+
override_nuget_package(
320+
project_dir=path,
321+
package="SpacetimeDB.BSATN.Runtime",
322+
source_dir=(STDB_DIR / "crates/bindings-csharp/BSATN.Runtime").absolute(),
323+
build_subdir="bin/Release"
324+
)
304325
override_nuget_package(
305326
project_dir=path,
306327
package="SpacetimeDB.ClientSDK",
@@ -316,9 +337,32 @@ def server_postprocess(self, server_path: Path):
316337
source_dir=(STDB_DIR / "crates/bindings-csharp/Runtime").absolute(),
317338
build_subdir="bin/Release"
318339
)
340+
override_nuget_package(
341+
project_dir=server_path,
342+
package="SpacetimeDB.BSATN.Runtime",
343+
source_dir=(STDB_DIR / "crates/bindings-csharp/BSATN.Runtime").absolute(),
344+
build_subdir="bin/Release"
345+
)
319346

320347
def test_quickstart(self):
321348
"""Run the C# quickstart guides for server and client."""
322349
if not smoketests.HAVE_DOTNET:
323350
self.skipTest("C# SDK requires .NET to be installed.")
324351
self._test_quickstart()
352+
353+
# We use the Rust client for testing the TypeScript server quickstart because
354+
# the TypeScript client quickstart is a React app, which is difficult to
355+
# smoketest.
356+
class TypeScript(Rust):
357+
lang = "typescript"
358+
client_lang = "rust"
359+
server_doc = STDB_DIR / "docs/docs/06-Server Module Languages/05-typescript-quickstart.md"
360+
server_file = "src/index.ts"
361+
362+
def server_postprocess(self, server_path: Path):
363+
build_typescript_sdk()
364+
pnpm("install", TYPESCRIPT_BINDINGS_PATH, cwd=server_path)
365+
366+
def test_quickstart(self):
367+
"""Run the TypeScript quickstart guides for server."""
368+
self._test_quickstart()

0 commit comments

Comments
 (0)