Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nexy7574 committed Nov 14, 2023
1 parent e4e5a75 commit 13f275f
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 19 deletions.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ dynamic = ["version", "dependencies"]
[project.optional-dependencies]
dev = [
"pytest>=7.4.3",
"fastapi>=0.104.1"
"fastapi>=0.104.1",
"pytest-dependency>=0.5.1",
]

[project.urls]
Expand Down
16 changes: 11 additions & 5 deletions src/dendritecli/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import secrets
import sys
import typing
from urllib.parse import quote
from pathlib import Path

import httpx
Expand Down Expand Up @@ -74,8 +75,7 @@ def __init__(self, access_token: str, client: typing.Optional[httpx.Client] = No
timeout=kwargs.pop("timeout", httpx.Timeout(connect=10, read=180, write=60, pool=10)),
follow_redirects=True,
max_redirects=10,
base_url=self.base_url,
app=kwargs.get("debug_app")
base_url=self.base_url
)

self.fulltext_reindex = self.full_text_reindex = self.reindex_events
Expand Down Expand Up @@ -115,6 +115,7 @@ def evacuate_room(self, room_id: str) -> dict[str, list[str]]:
:raises: httpx.HTTPError - if the request failed.
"""
log.info("Evacuating room %s", room_id)
room_id = quote(room_id)
response = self.client.post(f"/_dendrite/admin/evacuateRoom/{room_id}", timeout=None)
log.info("Finished evacuating room %s", room_id)
response.raise_for_status()
Expand All @@ -132,6 +133,7 @@ def evacuate_user(self, user_id: str) -> dict[str, list[str]]:
:raises: httpx.HTTPError - if the request failed.
"""
log.info("Evacuating user %s", user_id)
user_id = quote(user_id)
response = self.client.post(f"/_dendrite/admin/evacuateUser/{user_id}", timeout=None)
log.info("Finished evacuating user %s", user_id)
response.raise_for_status()
Expand Down Expand Up @@ -201,6 +203,7 @@ def refresh_devices(self, user_id: str) -> typing.Optional[dict]:
:raises: httpx.HTTPError - if the request failed.
"""
log.info("Requesting dendrite to refresh devices for user %s", user_id)
user_id = quote(user_id)
response = self.client.post(f"/_dendrite/admin/refreshDevices/{user_id}")
response.raise_for_status()
return response.json()
Expand All @@ -217,6 +220,7 @@ def purge_room(self, room_id: str) -> typing.Optional[dict]:
:raises: httpx.HTTPError - if the request failed.
"""
log.info("Requesting dendrite to purge room %s", room_id)
room_id = quote(room_id)
response = self.client.post(f"/_dendrite/admin/purgeRoom/{room_id}")
log.info("Finished purging room %s", room_id)
response.raise_for_status()
Expand Down Expand Up @@ -302,6 +306,7 @@ def register(self, nonce: typing.Optional[str] = None, **kwargs) -> typing.Union
admin = "admin" if kwargs["admin"] else "notadmin"
kwargs.setdefault("displayname", kwargs["username"])
kwargs["admin"] = admin
kwargs['nonce'] = nonce

mac.update(kwargs["nonce"].encode("utf-8"))
mac.update(b'\x00')
Expand Down Expand Up @@ -331,6 +336,7 @@ def whois(self, user_id: str) -> dict:
:raises: httpx.HTTPError - if the request failed.
"""
log.info("Fetching information about user %s", user_id)
user_id = quote(user_id)
response = self.client.get(f"/_matrix/client/v3/admin/whois/{user_id}")
log.info("Done fetching information about user %s", user_id)
response.raise_for_status()
Expand Down Expand Up @@ -362,7 +368,7 @@ def deactivate(self, user_id: str) -> None:

log.info("Deactivating user (step 2): Getting access token for user %s", user_id)
response = self.client.post(
"/_matrix/client/r0/login",
"/_matrix/client/v3/login",
json={
"type": "m.login.password",
"identifier": {"type": "m.id.user", "user": user_id},
Expand All @@ -377,7 +383,7 @@ def deactivate(self, user_id: str) -> None:
log.info("Beginning \"interactive\" deactivation of %s.", user_id)

initial_response = self.client.post(
"/_matrix/client/r0/account/deactivate",
"/_matrix/client/v3/account/deactivate",
auth=BearerAuth(access_token),
)
# This should yield HTTP 401 with our expected flow.
Expand Down Expand Up @@ -411,7 +417,7 @@ def deactivate(self, user_id: str) -> None:
}
log.info("Deactivating user (step 3): Deactivating user %s", user_id)
response = self.client.post(
"/_matrix/client/r0/account/deactivate",
"/_matrix/client/v3/account/deactivate",
auth=BearerAuth(access_token),
json=body,
)
Expand Down
137 changes: 124 additions & 13 deletions src/tests/_mock_server.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import secrets
import json

import fastapi
from pydantic import BaseModel
from fastapi.responses import JSONResponse
import time


class PasswordResetBody(BaseModel):
Expand All @@ -16,8 +16,9 @@ class PasswordResetBody(BaseModel):
)
app.state.users = {
"@example:example.local": {
"rooms": ["#example:example.local",],
"password": "example.password"
"rooms": ["!123456789abcdef:example.local",],
"password": "example.password",
"access_token": "example.access_token",
}
}
app.state.rooms = {
Expand All @@ -27,7 +28,7 @@ class PasswordResetBody(BaseModel):
}


@app.post("/_test/reset")
# @app.post("/_test/reset")
def reset_state():
app.state.users = {
"@example:example.local": {
Expand All @@ -40,11 +41,12 @@ def reset_state():
"users": ["@example:example.local", ],
}
}
print('Reset app state:', app.state.users, app.state.rooms)


@app.post("/_dendrite/admin/evacuateRoom/{room_id}")
def evacuate_room(room_id: str):
if room_id not in app.state.rooms:
async def evacuate_room(room_id: str):
if room_id not in app.state.rooms.keys():
raise fastapi.HTTPException(status_code=404)

affected = []
Expand All @@ -56,8 +58,8 @@ def evacuate_room(room_id: str):


@app.post("/_dendrite/admin/evacuateUser/{user_id}")
def evacuate_user(user_id: str):
if user_id not in app.state.users:
async def evacuate_user(user_id: str):
if user_id not in app.state.users.keys():
raise fastapi.HTTPException(status_code=404)

affected = []
Expand All @@ -69,7 +71,7 @@ def evacuate_user(user_id: str):


@app.post("/_dendrite/admin/resetPassword/{user_id}")
def reset_password(user_id: str, body: PasswordResetBody):
async def reset_password(user_id: str, body: PasswordResetBody):
if user_id not in app.state.users:
raise fastapi.HTTPException(status_code=404)

Expand All @@ -78,20 +80,20 @@ def reset_password(user_id: str, body: PasswordResetBody):


@app.post("/_dendrite/admin/fulltext/reindex")
def reindex():
async def reindex():
return {}


@app.post("/_dendrite/admin/refreshDevices/{user_id}")
def refresh_devices(user_id: str):
async def refresh_devices(user_id: str):
if user_id not in app.state.users:
raise fastapi.HTTPException(status_code=404)

return {}


@app.post("/_dendrite/admin/purgeRoom/{room_id}")
def purge_room(room_id: str):
async def purge_room(room_id: str):
if room_id not in app.state.rooms:
raise fastapi.HTTPException(status_code=404)

Expand All @@ -111,5 +113,114 @@ async def send_server_notice(req: fastapi.Request):
return {"event_id": "$" + secrets.token_urlsafe(16)}


@app.get('/_synapse/admin/v1/register')
async def register__get():
return {'nonce': 'nonce'}


@app.post("/_synapse/admin/v1/register")
async def register()
async def register(req: fastapi.Request):
body = await req.json()
if body.get('nonce') != 'nonce' or body.get('mac') is None:
raise fastapi.HTTPException(status_code=400)

# Assume, for the purposes of the test, the body is correct.
# This is a mock server, after all.
app.state.users[body['username']] = {
"rooms": [],
"password": body['password'],
"access_token": "example.access_token." + body['username'],
}
return {
"access_token": "example.access_token." + body['username'],
"home_server": "example.local",
"user_id": body['username'],
"device_id": "example.device_id." + body['username'],
}


@app.get("/_matrix/client/v3/admin/whois/{user_id}")
async def whois(user_id: str):
if user_id not in app.state.users:
raise fastapi.HTTPException(status_code=404)
return {
"devices": [],
"user_id": user_id,
}


@app.post('/_matrix/client/v3/login')
async def login(req: fastapi.Request):
body = await req.json()
if body['type'] != 'm.login.password':
raise fastapi.HTTPException(status_code=400)
if body['identifier']['type'] != 'm.id.user':
raise fastapi.HTTPException(status_code=400)
if body['identifier']['user'] not in app.state.users:
raise fastapi.HTTPException(status_code=400)
if body['password'] != app.state.users[body['identifier']['user']]['password']:
raise fastapi.HTTPException(status_code=400)
return {
"access_token": app.state.users[body['identifier']['user']]['access_token'],
"home_server": "example.local",
"user_id": body['identifier']['user'],
"device_id": "example.device_id." + body['identifier']['user'],
}


@app.post('/_matrix/client/v3/account/deactivate')
async def account_deactivate(req: fastapi.Request):
try:
body = await req.json()
except json.JSONDecodeError:
return JSONResponse(
{
'flows': [
{
'stages': ['m.login.password'],
}
],
'session': 'session_id'
},
401
)

if not body.get('auth'):
return JSONResponse(
{
'flows': [
{
'stages': ['m.login.password'],
}
],
'session': 'session_id'
},
401
)

if body['auth'].get('session') != 'session_id':
return JSONResponse(
{
'flows': [
{
'stages': ['m.login.password'],
}
],
'session': 'session_id'
},
401
)

if body['auth'].get('type') != 'm.login.password':
return JSONResponse(
{
'flows': [
{
'stages': ['m.login.password'],
}
],
'session': 'session_id'
},
401
)
return {}
Loading

0 comments on commit 13f275f

Please sign in to comment.