|
10 | 10 | from couchers.config import config |
11 | 11 | from couchers.crypto import random_hex |
12 | 12 | from couchers.db import session_scope |
13 | | -from couchers.models.logging import EventLog, EventSource |
| 13 | +from couchers.models.logging import EventLog, EventSource, NativeDiagnostics |
14 | 14 | from couchers.proto import bugs_pb2 |
15 | 15 | from couchers.proto.google.api import httpbody_pb2 |
16 | 16 | from tests.fixtures.db import generate_user |
@@ -395,6 +395,109 @@ def test_report_diagnostics_frontend_version(db): |
395 | 395 | assert events[0].version == "abc-def-123" |
396 | 396 |
|
397 | 397 |
|
| 398 | +def _get_native_diagnostics(session): |
| 399 | + return session.execute(select(NativeDiagnostics).order_by(NativeDiagnostics.id)).scalars().all() |
| 400 | + |
| 401 | + |
| 402 | +def test_check_native_status_anonymous(db): |
| 403 | + with bugs_session() as bugs: |
| 404 | + res = bugs.CheckNativeStatus( |
| 405 | + bugs_pb2.CheckNativeStatusReq( |
| 406 | + install_id="abc-123", |
| 407 | + idfv="idfv-1", |
| 408 | + user_state="logged_out", |
| 409 | + app_version="1.1.20", |
| 410 | + git_hash="deadbeef", |
| 411 | + native_build="42", |
| 412 | + platform="ios", |
| 413 | + os_version="17.4", |
| 414 | + device_name="Test iPhone", |
| 415 | + locale="en", |
| 416 | + ota_update_id="ota-1", |
| 417 | + runtime_version="rt-1", |
| 418 | + ota_channel="production", |
| 419 | + is_embedded=False, |
| 420 | + push_permission="granted", |
| 421 | + push_token="ExpoToken[xyz]", |
| 422 | + ) |
| 423 | + ) |
| 424 | + |
| 425 | + # Force-update decision logic isn't wired yet; for now the response is always permissive. |
| 426 | + assert res.update_required is False |
| 427 | + assert res.update_available is False |
| 428 | + assert res.update_url == "" |
| 429 | + assert res.update_message == "" |
| 430 | + |
| 431 | + with session_scope() as session: |
| 432 | + rows = _get_native_diagnostics(session) |
| 433 | + assert len(rows) == 1 |
| 434 | + row = rows[0] |
| 435 | + assert row.install_id == "abc-123" |
| 436 | + assert row.idfv == "idfv-1" |
| 437 | + assert row.android_id is None |
| 438 | + assert row.user_id is None |
| 439 | + assert row.user_state == "logged_out" |
| 440 | + assert row.app_version == "1.1.20" |
| 441 | + assert row.git_hash == "deadbeef" |
| 442 | + assert row.native_build == "42" |
| 443 | + assert row.platform == "ios" |
| 444 | + assert row.os_version == "17.4" |
| 445 | + assert row.device_name == "Test iPhone" |
| 446 | + assert row.ota_update_id == "ota-1" |
| 447 | + assert row.runtime_version == "rt-1" |
| 448 | + assert row.ota_channel == "production" |
| 449 | + assert row.is_embedded is False |
| 450 | + assert row.push_permission == "granted" |
| 451 | + assert row.push_token == "ExpoToken[xyz]" |
| 452 | + assert row.time_since_last_open_seconds is None |
| 453 | + |
| 454 | + |
| 455 | +def test_check_native_status_authenticated(db): |
| 456 | + user, token = generate_user() |
| 457 | + |
| 458 | + with bugs_session(token) as bugs: |
| 459 | + bugs.CheckNativeStatus( |
| 460 | + bugs_pb2.CheckNativeStatusReq( |
| 461 | + install_id="abc-123", |
| 462 | + user_state="authenticated", |
| 463 | + platform="android", |
| 464 | + android_id="ssaid-1", |
| 465 | + time_since_last_open_seconds=3600.0, |
| 466 | + ) |
| 467 | + ) |
| 468 | + |
| 469 | + with session_scope() as session: |
| 470 | + rows = _get_native_diagnostics(session) |
| 471 | + assert len(rows) == 1 |
| 472 | + assert rows[0].user_id == user.id |
| 473 | + assert rows[0].user_state == "authenticated" |
| 474 | + assert rows[0].platform == "android" |
| 475 | + assert rows[0].android_id == "ssaid-1" |
| 476 | + assert rows[0].time_since_last_open_seconds == pytest.approx(3600.0) |
| 477 | + |
| 478 | + |
| 479 | +def test_check_native_status_occurred_and_ota_created_at(db): |
| 480 | + occurred = timestamp_pb2.Timestamp() |
| 481 | + occurred.FromDatetime(datetime(2026, 1, 15, 10, 30, 0, tzinfo=UTC)) |
| 482 | + ota_created = timestamp_pb2.Timestamp() |
| 483 | + ota_created.FromDatetime(datetime(2026, 1, 10, 8, 0, 0, tzinfo=UTC)) |
| 484 | + |
| 485 | + with bugs_session() as bugs: |
| 486 | + bugs.CheckNativeStatus( |
| 487 | + bugs_pb2.CheckNativeStatusReq( |
| 488 | + install_id="abc-123", |
| 489 | + occurred=occurred, |
| 490 | + ota_created_at=ota_created, |
| 491 | + ) |
| 492 | + ) |
| 493 | + |
| 494 | + with session_scope() as session: |
| 495 | + rows = _get_native_diagnostics(session) |
| 496 | + assert len(rows) == 1 |
| 497 | + assert rows[0].occurred == datetime(2026, 1, 15, 10, 30, 0, tzinfo=UTC) |
| 498 | + assert rows[0].ota_created_at == datetime(2026, 1, 10, 8, 0, 0, tzinfo=UTC) |
| 499 | + |
| 500 | + |
398 | 501 | def _multipart_part_json(body, name): |
399 | 502 | """Extract and parse the JSON body of a named part from a multipart/mixed body.""" |
400 | 503 | marker = f'name="{name}"' |
|
0 commit comments