Spaces:
Paused
Paused
File size: 3,560 Bytes
57c13f4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | """Tests d'intégration de l'API AstroDynamics."""
import pytest
def test_health_returns_loaded_model(client) -> None:
response = client.get("/health")
assert response.status_code == 200
payload = response.json()
assert payload["status"] == "ok"
assert payload["environment"] == "LunarLander-v3"
assert payload["algorithm"] == "DQN"
assert payload["model"] == "dqn_lunarlander_final.zip"
@pytest.mark.parametrize("endpoint", ["/play", "/predict"])
def test_prediction_returns_valid_action(client, endpoint: str) -> None:
response = client.post(
endpoint,
json={
"observation": [
0.0, # position x
1.0, # position y
0.0, # vitesse x
-0.2, # vitesse y
0.0, # angle
0.0, # vitesse angulaire
0.0, # contact jambe gauche
0.0, # contact jambe droite
]
},
)
assert response.status_code == 200
payload = response.json()
assert payload["action"] in {0, 1, 2, 3}
assert isinstance(payload["action_label"], str)
assert len(payload["q_values"]) == 4
assert all(isinstance(value, float) for value in payload["q_values"])
assert isinstance(payload["circumstance"], str)
@pytest.mark.parametrize(
"observation",
[
[],
[0.0] * 7,
[0.0] * 9,
],
)
def test_prediction_rejects_invalid_observation_length(
client,
observation: list[float],
) -> None:
response = client.post(
"/predict",
json={"observation": observation},
)
assert response.status_code == 422
def test_metrics_are_initially_empty(client) -> None:
response = client.get("/metrics")
assert response.status_code == 200
payload = response.json()
assert payload["summary"]["number_of_episodes"] == 0
assert payload["summary"]["mean_reward"] is None
assert payload["summary"]["std_reward"] is None
assert payload["episodes"] == []
assert payload["action_distribution"] == {}
assert payload["decisions_by_circumstance"] == {}
@pytest.mark.integration
def test_episode_is_played_and_persisted(client) -> None:
"""
Joue un véritable épisode avec le modèle sauvegardé.
L'animation est désactivée pour accélérer la CI et limiter la RAM.
"""
response = client.post(
"/episodes",
json={
"seed": 42,
"capture_animation": False,
"frame_stride": 5,
},
)
assert response.status_code == 200
episode = response.json()
assert len(episode["episode_id"]) == 32
assert episode["seed"] == 42
assert episode["steps"] > 0
assert isinstance(episode["reward"], float)
assert isinstance(episode["success"], bool)
assert episode["animation_url"] is None
assert sum(episode["action_counts"].values()) == episode["steps"]
metrics_response = client.get("/metrics")
assert metrics_response.status_code == 200
metrics = metrics_response.json()
assert metrics["summary"]["number_of_episodes"] == 1
assert metrics["episodes"][0]["episode_id"] == episode["episode_id"]
assert metrics["episodes"][0]["seed"] == 42
assert metrics["episodes"][0]["reward"] == pytest.approx(
episode["reward"]
)
def test_unknown_animation_returns_404(client) -> None:
unknown_id = "0" * 32
response = client.get(
f"/episodes/{unknown_id}/animation"
)
assert response.status_code == 404 |