Skip to content

Commit b16bbc9

Browse files
authored
Merge branch 'exospherehost:main' into dev/nikiagarwal/landingpagetext
2 parents d867fe2 + 7d629cb commit b16bbc9

45 files changed

Lines changed: 1613 additions & 505 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy-kubernetes.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
kubectl delete secret exosphere-api-server-secrets --ignore-not-found
3232
kubectl create secret generic exosphere-api-server-secrets --from-literal=MONGO_URI=${{ secrets.API_SERVER_MONGO_URI }} --from-literal=MONGO_DATABASE_NAME=${{ secrets.API_SERVER_MONGO_DATABASE_NAME }} --from-literal=JWT_SECRET_KEY=${{ secrets.JWT_SECRET }}
3333
34-
kubectl create secret generic exosphere-state-manager-secrets --from-literal=MONGO_URI=${{ secrets.STATE_MANAGER_MONGO_URI }} --from-literal=MONGO_DATABASE_NAME=${{ secrets.STATE_MANAGER_MONGO_DATABASE_NAME }} --from-literal=STATE_MANAGER_SECRET=${{ secrets.STATE_MANAGER_SECRET }}
34+
kubectl create secret generic exosphere-state-manager-secrets --from-literal=MONGO_URI=${{ secrets.STATE_MANAGER_MONGO_URI }} --from-literal=MONGO_DATABASE_NAME=${{ secrets.STATE_MANAGER_MONGO_DATABASE_NAME }} --from-literal=STATE_MANAGER_SECRET=${{ secrets.STATE_MANAGER_SECRET }} --from-literal=SECRETS_ENCRYPTION_KEY=${{ secrets.SECRETS_ENCRYPTION_KEY }}
3535
3636
kubectl apply -f ./api-server-deployment.yaml
3737
kubectl apply -f ./api-server-service.yaml

.github/workflows/test-api-server.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ jobs:
4343
run: |
4444
uv sync --group dev
4545
46+
- name: Set JWT secret for tests
47+
run: echo "JWT_SECRET_KEY=test_secret" >> $GITHUB_ENV
48+
4649
- name: Run tests with pytest and coverage
4750
working-directory: api-server
4851
run: |

api-server/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies = [
2222
dev = [
2323
"ruff>=0.12.2",
2424
"pytest>=8.0.0",
25+
"pytest-asyncio>=0.25.0",
2526
"httpx>=0.27.0",
26-
"pytest-cov>=5.0.0"
27+
"pytest-cov>=5.0.0"
2728
]
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import pytest
2+
import jwt
3+
from starlette.responses import JSONResponse
4+
5+
from app.auth.controllers.create_token import create_token, JWT_SECRET_KEY, JWT_ALGORITHM
6+
from app.auth.models.token_request import TokenRequest
7+
from app.auth.models.token_response import TokenResponse
8+
from app.user.models.user_status_enum import UserStatusEnum
9+
from app.user.models.verification_status_enum import VerificationStatusEnum
10+
11+
12+
@pytest.mark.asyncio
13+
async def test_create_token_success(monkeypatch):
14+
monkeypatch.setenv("JWT_SECRET_KEY", "test_secret")
15+
16+
class DummyUser:
17+
id = "507f1f77bcf86cd799439011"
18+
name = "John"
19+
type = "admin"
20+
verification_status = VerificationStatusEnum.VERIFIED.value
21+
status = UserStatusEnum.ACTIVE.value
22+
def verify_credential(self, cred):
23+
return True
24+
25+
async def mock_find_one(_query):
26+
return DummyUser()
27+
28+
async def mock_project_get(_id):
29+
return None
30+
31+
class MockUser:
32+
identifier = "identifier"
33+
find_one = staticmethod(mock_find_one)
34+
35+
class MockProject:
36+
get = staticmethod(mock_project_get)
37+
38+
monkeypatch.setattr("app.auth.controllers.create_token.User", MockUser)
39+
monkeypatch.setattr("app.auth.controllers.create_token.Project", MockProject)
40+
41+
req = TokenRequest(identifier="user", credential="pass", project=None, satellites=None)
42+
res = await create_token(req, "req-id")
43+
44+
assert isinstance(res, TokenResponse)
45+
decoded = jwt.decode(res.access_token, JWT_SECRET_KEY, algorithms=[JWT_ALGORITHM])
46+
assert decoded["user_id"] == "507f1f77bcf86cd799439011"
47+
assert decoded["token_type"] == "access"
48+
49+
50+
@pytest.mark.asyncio
51+
async def test_create_token_invalid_user(monkeypatch):
52+
async def mock_find_one(_query):
53+
return None
54+
55+
class MockUser:
56+
identifier = "identifier"
57+
find_one = staticmethod(mock_find_one)
58+
59+
monkeypatch.setattr("app.auth.controllers.create_token.User", MockUser)
60+
61+
req = TokenRequest(identifier="bad", credential="pass", project=None, satellites=None)
62+
res = await create_token(req, "req-id")
63+
64+
assert isinstance(res, JSONResponse)
65+
assert res.status_code == 404
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import pytest
2+
import jwt
3+
from starlette.responses import JSONResponse
4+
5+
from app.auth.controllers.refresh_access_token import (
6+
refresh_access_token,
7+
JWT_SECRET_KEY,
8+
JWT_ALGORITHM,
9+
JWT_EXPIRES_IN
10+
)
11+
from app.auth.models.refresh_token_request import RefreshTokenRequest
12+
from app.auth.models.token_response import TokenResponse
13+
from app.auth.models.token_type_enum import TokenType
14+
from app.user.models.user_status_enum import UserStatusEnum
15+
from app.user.models.verification_status_enum import VerificationStatusEnum
16+
17+
18+
@pytest.mark.asyncio
19+
async def test_refresh_access_token_success(monkeypatch):
20+
monkeypatch.setenv("JWT_SECRET_KEY", "test_secret")
21+
22+
class DummyUser:
23+
id = "507f1f77bcf86cd799439011"
24+
name = "John"
25+
type = "admin"
26+
verification_status = VerificationStatusEnum.VERIFIED.value
27+
status = UserStatusEnum.ACTIVE.value
28+
29+
class MockUser:
30+
@staticmethod
31+
async def get(_id):
32+
return DummyUser()
33+
34+
class MockProject:
35+
@staticmethod
36+
async def get(_id):
37+
return None
38+
39+
monkeypatch.setattr("app.auth.controllers.refresh_access_token.User", MockUser)
40+
monkeypatch.setattr("app.auth.controllers.refresh_access_token.Project", MockProject)
41+
42+
import datetime
43+
payload = {
44+
"user_id": "507f1f77bcf86cd799439011",
45+
"token_type": TokenType.refresh.value,
46+
"exp": int((datetime.datetime.now() + datetime.timedelta(seconds=JWT_EXPIRES_IN)).timestamp())
47+
}
48+
token = jwt.encode(payload, JWT_SECRET_KEY, algorithm=JWT_ALGORITHM)
49+
req = RefreshTokenRequest(refresh_token=token)
50+
res = await refresh_access_token(req, "req-id")
51+
52+
assert isinstance(res, TokenResponse)
53+
decoded = jwt.decode(res.access_token, JWT_SECRET_KEY, algorithms=[JWT_ALGORITHM])
54+
assert decoded["user_id"] == "507f1f77bcf86cd799439011"
55+
assert decoded["token_type"] == "access"
56+
57+
58+
@pytest.mark.asyncio
59+
async def test_refresh_access_token_invalid_token(monkeypatch):
60+
monkeypatch.setenv("JWT_SECRET_KEY", "test_secret")
61+
62+
bad_token = jwt.encode({"token_type": "wrong"}, JWT_SECRET_KEY, algorithm=JWT_ALGORITHM)
63+
req = RefreshTokenRequest(refresh_token=bad_token)
64+
res = await refresh_access_token(req, "req-id")
65+
66+
assert isinstance(res, JSONResponse)
67+
assert res.status_code == 401

api-server/uv.lock

Lines changed: 35 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)