169 lines
5.1 KiB
Python
169 lines
5.1 KiB
Python
from datetime import timedelta
|
|
|
|
from app import db
|
|
from app.models.gamestate import GameState
|
|
from app.models.login_attempt import LoginAttempt
|
|
from app.models.user import User
|
|
from app.routes.login_routes import utcnow
|
|
|
|
|
|
def test_dashboard_requires_login(client):
|
|
response = client.get("/dashboard")
|
|
|
|
assert response.status_code == 302
|
|
assert "/login" in response.headers["Location"]
|
|
|
|
|
|
def test_register_creates_user_and_redirects_to_login(client, app_instance):
|
|
response = client.post(
|
|
"/register",
|
|
data={"username": "newuser", "password": "Password123!"},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
assert "/login" in response.headers["Location"]
|
|
|
|
with app_instance.app_context():
|
|
saved_user = User.query.filter_by(username="newuser").first()
|
|
assert saved_user is not None
|
|
|
|
|
|
def test_login_blocks_requests_from_a_locked_ip(client, app_instance, user_factory):
|
|
user_factory(username="lockeduser", password="Password123!")
|
|
|
|
with app_instance.app_context():
|
|
db.session.add(
|
|
LoginAttempt(
|
|
ip_address="203.0.113.10",
|
|
failed_attempts=4,
|
|
locked_until=utcnow() + timedelta(minutes=5),
|
|
)
|
|
)
|
|
db.session.commit()
|
|
|
|
response = client.post(
|
|
"/login",
|
|
data={"username": "lockeduser", "password": "Password123!"},
|
|
headers={"X-Forwarded-For": "203.0.113.10"},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert b"Too many failed login attempts" in response.data
|
|
|
|
|
|
def test_check_endpoint_applies_different_rules_for_queens_and_rooks(client):
|
|
board = [
|
|
[1, 0, 0, 0],
|
|
[0, 0, 0, 0],
|
|
[0, 0, 0, 0],
|
|
[0, 0, 0, 0],
|
|
]
|
|
|
|
queens_response = client.post(
|
|
"/check",
|
|
json={"board": board, "row": 1, "col": 1, "mode": "queens"},
|
|
)
|
|
rooks_response = client.post(
|
|
"/check",
|
|
json={"board": board, "row": 1, "col": 1, "mode": "rooks"},
|
|
)
|
|
|
|
assert queens_response.status_code == 200
|
|
assert rooks_response.status_code == 200
|
|
assert queens_response.get_json() == {"valid": False}
|
|
assert rooks_response.get_json() == {"valid": True}
|
|
|
|
|
|
def test_solve_endpoint_returns_one_valid_4x4_queens_solution(client):
|
|
response = client.get("/solve?size=4")
|
|
|
|
assert response.status_code == 200
|
|
assert tuple(response.get_json()) in {
|
|
(1, 3, 0, 2),
|
|
(2, 0, 3, 1),
|
|
}
|
|
|
|
|
|
def test_authenticated_user_can_save_and_load_a_game_state(client, app_instance, user_factory, login_as):
|
|
user_factory(username="alice", password="Password123!")
|
|
|
|
login_response = login_as("alice", "Password123!")
|
|
assert login_response.status_code == 302
|
|
|
|
payload = {
|
|
"board": [
|
|
[0, 1, 0, 0],
|
|
[0, 0, 0, 1],
|
|
[1, 0, 0, 0],
|
|
[0, 0, 1, 0],
|
|
],
|
|
"mode": "queens",
|
|
"boardSize": 4,
|
|
"saveName": "test-save",
|
|
"saveNote": "unit integration save",
|
|
"isFavorite": False,
|
|
"stepCount": 7,
|
|
"elapsedSeconds": 42,
|
|
"isSolved": True,
|
|
}
|
|
|
|
save_response = client.post("/save", json=payload)
|
|
assert save_response.status_code == 200
|
|
|
|
saved_data = save_response.get_json()["save_point"]
|
|
load_response = client.get(f"/load/{saved_data['id']}")
|
|
|
|
assert load_response.status_code == 200
|
|
loaded_data = load_response.get_json()
|
|
assert loaded_data["board"] == payload["board"]
|
|
assert loaded_data["mode"] == payload["mode"]
|
|
assert loaded_data["step_count"] == payload["stepCount"]
|
|
assert loaded_data["elapsed_seconds"] == payload["elapsedSeconds"]
|
|
assert loaded_data["is_solved"] is True
|
|
|
|
with app_instance.app_context():
|
|
assert GameState.query.count() == 1
|
|
|
|
|
|
def test_favorite_toggle_changes_filtered_save_point_list(client, user_factory, login_as):
|
|
user_factory(username="favuser", password="Password123!")
|
|
|
|
login_response = login_as("favuser", "Password123!")
|
|
assert login_response.status_code == 302
|
|
|
|
save_response = client.post(
|
|
"/save",
|
|
json={
|
|
"board": [
|
|
[1, 0, 0, 0],
|
|
[0, 1, 0, 0],
|
|
[0, 0, 0, 0],
|
|
[0, 0, 0, 0],
|
|
],
|
|
"mode": "rooks",
|
|
"boardSize": 4,
|
|
"saveName": "favorite-candidate",
|
|
"saveNote": "",
|
|
"isFavorite": False,
|
|
"stepCount": 2,
|
|
"elapsedSeconds": 5,
|
|
"isSolved": False,
|
|
},
|
|
)
|
|
|
|
save_id = save_response.get_json()["save_point"]["id"]
|
|
|
|
filtered_before = client.get("/save_points?favorites_only=true")
|
|
assert filtered_before.status_code == 200
|
|
assert filtered_before.get_json() == []
|
|
|
|
toggle_response = client.post(f"/save_points/{save_id}/favorite")
|
|
assert toggle_response.status_code == 200
|
|
assert toggle_response.get_json()["is_favorite"] is True
|
|
|
|
filtered_after = client.get("/save_points?favorites_only=true")
|
|
assert filtered_after.status_code == 200
|
|
assert len(filtered_after.get_json()) == 1
|
|
assert filtered_after.get_json()[0]["id"] == save_id
|