|
1 | 1 | import argparse |
| 2 | +from datetime import datetime, timezone |
2 | 3 | from textwrap import dedent |
3 | 4 | from typing import List, Optional, Tuple |
4 | 5 | from unittest.mock import Mock |
|
9 | 10 | from dstack._internal.cli.services.configurators import get_run_configurator_class |
10 | 11 | from dstack._internal.cli.services.configurators.run import ( |
11 | 12 | BaseRunConfigurator, |
| 13 | + _get_apply_status, |
| 14 | + _get_apply_wait_renderables, |
12 | 15 | render_run_spec_diff, |
13 | 16 | ) |
14 | 17 | from dstack._internal.core.errors import ConfigurationError |
|
18 | 21 | BaseRunConfiguration, |
19 | 22 | DevEnvironmentConfiguration, |
20 | 23 | PortMapping, |
| 24 | + ScalingSpec, |
| 25 | + ServiceConfiguration, |
21 | 26 | TaskConfiguration, |
22 | 27 | ) |
23 | 28 | from dstack._internal.core.models.envs import Env |
24 | 29 | from dstack._internal.core.models.profiles import Profile |
25 | | -from dstack._internal.server.testing.common import get_run_spec |
| 30 | +from dstack._internal.core.models.resources import Range |
| 31 | +from dstack._internal.core.models.runs import RunStatus, ServiceSpec |
| 32 | +from dstack._internal.server.services import encryption # noqa: F401 # import for side-effect |
| 33 | +from dstack._internal.server.services.runs import run_model_to_run |
| 34 | +from dstack._internal.server.testing.common import ( |
| 35 | + create_project, |
| 36 | + create_repo, |
| 37 | + create_run, |
| 38 | + create_user, |
| 39 | + get_run_spec, |
| 40 | +) |
| 41 | +from dstack.api import Run |
| 42 | +from dstack.api.server import APIClient |
26 | 43 |
|
27 | 44 |
|
28 | 45 | class TestApplyArgs: |
@@ -401,3 +418,74 @@ def test_no_diff(self): |
401 | 418 | old = get_run_spec(run_name="test", repo_id="test") |
402 | 419 | new = get_run_spec(run_name="test", repo_id="test") |
403 | 420 | assert render_run_spec_diff(old, new) is None |
| 421 | + |
| 422 | + |
| 423 | +@pytest.mark.asyncio |
| 424 | +@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True) |
| 425 | +class TestApplyStatusHelpers: |
| 426 | + async def test_waiting_for_requests_status_and_renderables(self, session): |
| 427 | + project = await create_project(session=session) |
| 428 | + user = await create_user(session=session) |
| 429 | + repo = await create_repo(session=session, project_id=project.id) |
| 430 | + run_spec = get_run_spec( |
| 431 | + run_name="service-run", |
| 432 | + repo_id=repo.name, |
| 433 | + configuration=ServiceConfiguration( |
| 434 | + type="service", |
| 435 | + image="ubuntu:latest", |
| 436 | + commands=["echo hello"], |
| 437 | + port=80, |
| 438 | + replicas=Range[int](min=0, max=1), |
| 439 | + scaling=ScalingSpec(metric="rps", target=1), |
| 440 | + ), |
| 441 | + ) |
| 442 | + run_model = await create_run( |
| 443 | + session=session, |
| 444 | + project=project, |
| 445 | + repo=repo, |
| 446 | + user=user, |
| 447 | + run_name="service-run", |
| 448 | + run_spec=run_spec, |
| 449 | + status=RunStatus.PENDING, |
| 450 | + ) |
| 451 | + run_model.service_spec = ServiceSpec(url="/proxy/services/test/service-run/").json() |
| 452 | + await session.commit() |
| 453 | + await session.refresh(run_model) |
| 454 | + |
| 455 | + api_run = Run( |
| 456 | + api_client=Mock(spec=APIClient, base_url="http://127.0.0.1:3000"), |
| 457 | + project=project.name, |
| 458 | + run=run_model_to_run(run_model), |
| 459 | + ) |
| 460 | + |
| 461 | + assert _get_apply_status(api_run) == "[code]service-run[/] is waiting for requests..." |
| 462 | + assert _get_apply_wait_renderables(api_run) == [ |
| 463 | + "Service URL: [link=http://127.0.0.1:3000/proxy/services/test/service-run/]http://127.0.0.1:3000/proxy/services/test/service-run/[/]" |
| 464 | + ] |
| 465 | + |
| 466 | + async def test_waiting_for_schedule_status_and_renderables(self, session): |
| 467 | + project = await create_project(session=session) |
| 468 | + user = await create_user(session=session) |
| 469 | + repo = await create_repo(session=session, project_id=project.id) |
| 470 | + run_model = await create_run( |
| 471 | + session=session, |
| 472 | + project=project, |
| 473 | + repo=repo, |
| 474 | + user=user, |
| 475 | + run_name="scheduled-run", |
| 476 | + status=RunStatus.PENDING, |
| 477 | + next_triggered_at=datetime(2023, 1, 2, 3, 10, tzinfo=timezone.utc), |
| 478 | + ) |
| 479 | + await session.refresh(run_model) |
| 480 | + |
| 481 | + api_run = Run( |
| 482 | + api_client=Mock(spec=APIClient), |
| 483 | + project=project.name, |
| 484 | + run=run_model_to_run(run_model), |
| 485 | + ) |
| 486 | + next_run = datetime(2023, 1, 2, 3, 10, tzinfo=timezone.utc) |
| 487 | + api_run._run.next_triggered_at = next_run |
| 488 | + |
| 489 | + assert _get_apply_status(api_run) == "[code]scheduled-run[/] is waiting for schedule..." |
| 490 | + expected_next_run = next_run.astimezone().strftime("%Y-%m-%d %H:%M %Z") |
| 491 | + assert _get_apply_wait_renderables(api_run) == [f"Next run: {expected_next_run}"] |
0 commit comments