add tests to email

This commit is contained in:
2025-11-26 23:33:12 -03:00
parent 5859248781
commit ab7e4ea38b
9 changed files with 1040 additions and 718 deletions

View File

@@ -103,6 +103,7 @@ def unlink(org_id: str, user_id: str):
def _create_user(user: User, org: Org) -> bool:
now_ = now()
user_id = uuid4()
email_verified = '@users.noreply.saladeaula.digital' in user.email
try:
with dyn.transact_writer() as transact:
@@ -111,7 +112,7 @@ def _create_user(user: User, org: Org) -> bool:
**user.model_dump(),
'id': user_id,
'sk': '0',
'email_verified': False,
'email_verified': email_verified,
'tenant_id': {org.id},
# Post-migration: uncomment the folloing line
# 'org_id': {org.id},
@@ -123,7 +124,7 @@ def _create_user(user: User, org: Org) -> bool:
'id': user_id,
# Post-migration: rename `emails` to `EMAIL`
'sk': f'emails#{user.email}',
'email_verified': False,
'email_verified': email_verified,
'email_primary': True,
'created_at': now_,
}

View File

@@ -68,8 +68,8 @@ def add(
)
transact.put(
item={
'id': 'EMAIL_VERIFICATION',
'sk': uuid4(),
'id': user_id,
'sk': f'EMAIL_VERIFICATION#{uuid4()}',
'name': name,
'email': email,
'user_id': user_id,
@@ -81,6 +81,27 @@ def add(
return JSONResponse(status_code=HTTPStatus.CREATED)
@router.post('/<user_id>/emails/<email>/request-verification')
def request_verification(user_id: str, email: Annotated[EmailStr, Path]):
now_ = now()
name = dyn.collection.get_item(
KeyPair(user_id, SortKey('0', path_spec='name')),
raise_on_error=False,
)
dyn.put_item(
item={
'id': user_id,
'sk': f'EMAIL_VERIFICATION#{uuid4()}',
'name': name,
'email': email,
'user_id': user_id,
'ttl': ttl(start_dt=now_, days=30),
'created_at': now_,
}
)
return JSONResponse(status_code=HTTPStatus.NO_CONTENT)
class EmailVerificationNotFoundError(NotFoundError): ...
@@ -88,15 +109,18 @@ class EmailVerificationNotFoundError(NotFoundError): ...
def verify(user_id: str, hash: str):
email = dyn.collection.get_item(
KeyPair(
pk='EMAIL_VERIFICATION',
sk=SortKey(hash, path_spec='email'),
pk=user_id,
sk=SortKey(f'EMAIL_VERIFICATION#{hash}', path_spec='email'),
),
exc_cls=EmailVerificationNotFoundError,
)
with dyn.transact_writer() as transact:
transact.delete(key=KeyPair('EMAIL_VERIFICATION', hash))
transact.delete(
key=KeyPair(user_id, f'EMAIL_VERIFICATION#{hash}'),
)
transact.update(
# Post-migration (users): rename `emails` to `EMAIL`
key=KeyPair(user_id, f'emails#{email}'),
update_expr='SET email_verified = :true, updated_at = :now',
expr_attr_values={

View File

@@ -1,7 +1,9 @@
import json
from http import HTTPMethod, HTTPStatus
from ..conftest import HttpApiProxy, LambdaContext
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
from ...conftest import HttpApiProxy, LambdaContext
def test_get_emails(
@@ -23,6 +25,35 @@ def test_get_emails(
assert len(body['items']) == 2
def test_add_email(
app,
seeds,
http_api_proxy: HttpApiProxy,
lambda_context: LambdaContext,
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
):
r = app.lambda_handler(
http_api_proxy(
raw_path='/users/15bacf02-1535-4bee-9022-19d106fd7518/emails',
method=HTTPMethod.POST,
body={
'email': 'osergiosiqueira+pytest@gmail.com',
},
),
lambda_context,
)
assert r['statusCode'] == HTTPStatus.CREATED
email_verification = dynamodb_persistence_layer.collection.query(
KeyPair(
'15bacf02-1535-4bee-9022-19d106fd7518',
'EMAIL_VERIFICATION',
)
)
assert 'name' in email_verification['items'][0]
assert email_verification['items'][0]['email'] == 'osergiosiqueira+pytest@gmail.com'
def test_email_as_primary(
app,
seeds,
@@ -45,7 +76,7 @@ def test_email_as_primary(
assert r['statusCode'] == HTTPStatus.NO_CONTENT
def test_get_orgs(
def test_remove_emal(
app,
seeds,
http_api_proxy: HttpApiProxy,
@@ -53,10 +84,10 @@ def test_get_orgs(
):
r = app.lambda_handler(
http_api_proxy(
raw_path='/users/213a6682-2c59-4404-9189-12eec0a846d4/orgs',
method=HTTPMethod.GET,
raw_path='/users/15bacf02-1535-4bee-9022-19d106fd7518/emails/osergiosiqueira@gmail.com',
method=HTTPMethod.DELETE,
),
lambda_context,
)
assert r['statusCode'] == HTTPStatus.OK
assert r['statusCode'] == HTTPStatus.NO_CONTENT

View File

@@ -0,0 +1,20 @@
from http import HTTPMethod, HTTPStatus
from ...conftest import HttpApiProxy, LambdaContext
def test_get_orgs(
app,
seeds,
http_api_proxy: HttpApiProxy,
lambda_context: LambdaContext,
):
r = app.lambda_handler(
http_api_proxy(
raw_path='/users/213a6682-2c59-4404-9189-12eec0a846d4/orgs',
method=HTTPMethod.GET,
),
lambda_context,
)
assert r['statusCode'] == HTTPStatus.OK

View File

@@ -93,15 +93,15 @@ wheels = [
[[package]]
name = "aws-lambda-powertools"
version = "3.20.0"
version = "3.23.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jmespath" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/83/c3/ee5b3ed77be236ff0accafabb137a3a1431925ae11b50fc31fab09d60db1/aws_lambda_powertools-3.20.0.tar.gz", hash = "sha256:97cdaa52060972a59d0e769e197d6bffe5a1869c54b6a0c6c69bf31656de5bdd", size = 697927, upload-time = "2025-09-09T09:56:11.78Z" }
sdist = { url = "https://files.pythonhosted.org/packages/38/24/78f320a310d98df8c831e15c5f04fec20ba4958253deb165ab2d10d3392b/aws_lambda_powertools-3.23.0.tar.gz", hash = "sha256:30ab45960989dd75a4d84de4f156509458f8782038d532eee2f815488d7cc929", size = 702835, upload-time = "2025-11-13T16:44:23.659Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/37/d036d4c33f0d943dbd5257d2c26f5a60375452705526b34977357f8faebf/aws_lambda_powertools-3.20.0-py3-none-any.whl", hash = "sha256:1641eaf38e59470a28262e34d5d447bf6713223ea8896bf5ce442ffb3418c9c9", size = 842168, upload-time = "2025-09-09T09:56:09.847Z" },
{ url = "https://files.pythonhosted.org/packages/70/48/f59597b0acbe3bcd829ae5b13b49a29039c5b2a5a6771f765ad3f3f576a3/aws_lambda_powertools-3.23.0-py3-none-any.whl", hash = "sha256:f3d16f1b0304c686cc956ecf0f6f8907d21992a4a5070e2388c21571d8c84cc2", size = 848256, upload-time = "2025-11-13T16:44:21.459Z" },
]
[package.optional-dependencies]
@@ -622,7 +622,7 @@ dependencies = [
requires-dist = [
{ name = "arnparse", specifier = ">=0.0.2" },
{ name = "authlib", specifier = ">=1.6.5" },
{ name = "aws-lambda-powertools", extras = ["all"], specifier = ">=3.18.0" },
{ name = "aws-lambda-powertools", extras = ["all"], specifier = ">=3.23.0" },
{ name = "dictdiffer", specifier = ">=0.9.0" },
{ name = "ftfy", specifier = ">=6.3.1" },
{ name = "glom", specifier = ">=24.11.0" },

View File

@@ -1,6 +1,7 @@
import { LockIcon } from 'lucide-react'
import type { Route } from './+types'
import { LockIcon } from 'lucide-react'
export function meta({}: Route.MetaArgs) {
return [{ title: 'Acesso negado · EDUSEG®' }]
}

View File

@@ -8,7 +8,7 @@ authors = [
]
requires-python = ">=3.12"
dependencies = [
"aws-lambda-powertools[all]>=3.18.0",
"aws-lambda-powertools[all]>=3.23.0",
"ftfy>=6.3.1",
"glom>=24.11.0",
"orjson>=3.10.15",

1645
layercake/uv.lock generated

File diff suppressed because it is too large Load Diff