Skip to content

Commit cab59ca

Browse files
mishushakovclaude
andauthored
fix(js-sdk): pass getMetrics start/end as query params (#1427)
## Description `Sandbox.getMetrics()` in the JS SDK passed the `start` and `end` options as path parameters instead of query parameters, so openapi-fetch never serialized them and the requested time range was silently ignored. They are now sent under `query`, matching the OpenAPI spec; the Python SDKs already passed them correctly. The metrics tests across JS and Python (sync/async) now assert that returned metrics fall within the requested window (with slack for 5s metric-bucket alignment) and that a window from before the sandbox existed returns no metrics, which would have caught this regression. ## Usage ```ts const start = new Date(Date.now() - 60_000) const metrics = await sandbox.getMetrics({ start, end: new Date() }) // metrics are now actually limited to the requested time range ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 32880d6 commit cab59ca

5 files changed

Lines changed: 112 additions & 7 deletions

File tree

.changeset/brave-pandas-listen.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"e2b": patch
3+
---
4+
5+
Fix `Sandbox.getMetrics()` sending `start` and `end` as path parameters instead of query parameters, which caused the requested time range to be silently ignored

packages/js-sdk/src/sandbox/sandboxApi.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,8 @@ export class SandboxApi {
798798
params: {
799799
path: {
800800
sandboxID: sandboxId,
801+
},
802+
query: {
801803
start,
802804
end,
803805
},

packages/js-sdk/tests/sandbox/metrics.test.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ sandboxTest.skipIf(isDebug)(
77
'sbx metrics',
88
{ timeout: 60_000 },
99
async ({ sandbox }) => {
10-
const now = new Date()
11-
let metrics: SandboxMetrics[]
10+
// Wait for the sandbox to have some metrics
11+
let metrics: SandboxMetrics[] = []
1212
for (let i = 0; i < 60; i++) {
1313
metrics = await sandbox.getMetrics()
1414
if (metrics.length > 0) {
@@ -25,8 +25,45 @@ sandboxTest.skipIf(isDebug)(
2525
expect(metric.memUsed).toBeDefined()
2626
expect(metric.cpuUsedPct).toBeDefined()
2727
expect(metric.cpuCount).toBeDefined()
28+
}
29+
)
30+
31+
sandboxTest.skipIf(isDebug)(
32+
'sbx metrics time range',
33+
{ timeout: 60_000 },
34+
async ({ sandbox }) => {
35+
const start = new Date()
36+
37+
// Wait for the sandbox to have some metrics within the test's time window
38+
let metrics: SandboxMetrics[] = []
39+
let end = new Date()
40+
for (let i = 0; i < 60; i++) {
41+
end = new Date()
42+
metrics = await sandbox.getMetrics({ start, end })
43+
if (metrics.length > 0) {
44+
break
45+
}
46+
await wait(500)
47+
}
2848

29-
metrics = await sandbox.getMetrics({ start: now, end: new Date() })
3049
expect(metrics.length).toBeGreaterThan(0)
50+
51+
// All returned metrics must fall within the requested time range
52+
// (10s slack — metric timestamps are aligned to collection buckets,
53+
// currently 5s, and the query params are second-precision)
54+
const slackMs = 10_000
55+
for (const m of metrics) {
56+
expect(m.timestamp.getTime()).toBeGreaterThanOrEqual(
57+
start.getTime() - slackMs
58+
)
59+
expect(m.timestamp.getTime()).toBeLessThanOrEqual(end.getTime() + slackMs)
60+
}
61+
62+
// A time range from before the sandbox existed must return no metrics
63+
const noMetrics = await sandbox.getMetrics({
64+
start: new Date(start.getTime() - 60 * 60 * 1000),
65+
end: new Date(start.getTime() - 30 * 60 * 1000),
66+
})
67+
expect(noMetrics).toHaveLength(0)
3168
}
3269
)

packages/python-sdk/tests/async/sandbox_async/test_metrics.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
@pytest.mark.skip_debug()
88
@pytest.mark.timeout(60)
99
async def test_sbx_metrics(async_sandbox_factory):
10-
start_time = datetime.datetime.now()
1110
sbx = await async_sandbox_factory(timeout=60)
1211

1312
# Wait for the sandbox to have some metrics
@@ -28,5 +27,36 @@ async def test_sbx_metrics(async_sandbox_factory):
2827
assert metric.disk_used is not None
2928
assert metric.disk_total is not None
3029

31-
metrics = await sbx.get_metrics(start=start_time, end=datetime.datetime.now())
30+
31+
@pytest.mark.skip_debug()
32+
@pytest.mark.timeout(60)
33+
async def test_sbx_metrics_time_range(async_sandbox_factory):
34+
start_time = datetime.datetime.now(datetime.timezone.utc)
35+
sbx = await async_sandbox_factory(timeout=60)
36+
37+
# Wait for the sandbox to have some metrics within the test's time window
38+
metrics = []
39+
end_time = start_time
40+
for _ in range(60):
41+
end_time = datetime.datetime.now(datetime.timezone.utc)
42+
metrics = await sbx.get_metrics(start=start_time, end=end_time)
43+
if len(metrics) > 0:
44+
break
45+
await asyncio.sleep(0.5)
46+
3247
assert len(metrics) > 0
48+
49+
# All returned metrics must fall within the requested time range
50+
# (10s slack - metric timestamps are aligned to collection buckets,
51+
# currently 5s, and the query params are second-precision)
52+
slack = 10
53+
for metric in metrics:
54+
assert metric.timestamp.timestamp() >= start_time.timestamp() - slack
55+
assert metric.timestamp.timestamp() <= end_time.timestamp() + slack
56+
57+
# A time range from before the sandbox existed must return no metrics
58+
metrics = await sbx.get_metrics(
59+
start=start_time - datetime.timedelta(hours=1),
60+
end=start_time - datetime.timedelta(minutes=30),
61+
)
62+
assert len(metrics) == 0

packages/python-sdk/tests/sync/sandbox_sync/test_metrics.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
@pytest.mark.skip_debug()
88
@pytest.mark.timeout(60)
99
def test_sbx_metrics(sandbox_factory) -> None:
10-
start_time = datetime.datetime.now()
1110
sbx = sandbox_factory(timeout=60)
11+
1212
# Wait for the sandbox to have some metrics
1313
metrics = []
1414
for _ in range(60):
@@ -27,5 +27,36 @@ def test_sbx_metrics(sandbox_factory) -> None:
2727
assert metric.disk_used is not None
2828
assert metric.disk_total is not None
2929

30-
metrics = sbx.get_metrics(start=start_time, end=datetime.datetime.now())
30+
31+
@pytest.mark.skip_debug()
32+
@pytest.mark.timeout(60)
33+
def test_sbx_metrics_time_range(sandbox_factory) -> None:
34+
start_time = datetime.datetime.now(datetime.timezone.utc)
35+
sbx = sandbox_factory(timeout=60)
36+
37+
# Wait for the sandbox to have some metrics within the test's time window
38+
metrics = []
39+
end_time = start_time
40+
for _ in range(60):
41+
end_time = datetime.datetime.now(datetime.timezone.utc)
42+
metrics = sbx.get_metrics(start=start_time, end=end_time)
43+
if len(metrics) > 0:
44+
break
45+
time.sleep(0.5)
46+
3147
assert len(metrics) > 0
48+
49+
# All returned metrics must fall within the requested time range
50+
# (10s slack - metric timestamps are aligned to collection buckets,
51+
# currently 5s, and the query params are second-precision)
52+
slack = 10
53+
for metric in metrics:
54+
assert metric.timestamp.timestamp() >= start_time.timestamp() - slack
55+
assert metric.timestamp.timestamp() <= end_time.timestamp() + slack
56+
57+
# A time range from before the sandbox existed must return no metrics
58+
metrics = sbx.get_metrics(
59+
start=start_time - datetime.timedelta(hours=1),
60+
end=start_time - datetime.timedelta(minutes=30),
61+
)
62+
assert len(metrics) == 0

0 commit comments

Comments
 (0)