FAQ | This is a LIVE service | Changelog

Skip to content
Commits on Source (6)
......@@ -8,9 +8,6 @@ include:
- project: "uis/devops/continuous-delivery/ci-templates"
file: "/auto-devops/release-it.yml"
ref: v5.1.0
- project: "uis/devops/continuous-delivery/ci-templates"
file: "/commitlint.yml"
ref: v5.1.0
variables:
DAST_DISABLED: "1"
......
# Changelog
## [0.4.0](https://gitlab.developers.cam.ac.uk/uis/devops/iam/activate-account/api/compare/0.3.1...0.4.0) (2024-11-01)
### Features
* ensure crsid field is always lower cased ([8ce476a](https://gitlab.developers.cam.ac.uk/uis/devops/iam/activate-account/api/commit/8ce476a0acda3663b15b1892fc337d4c57687775))
### Bug Fixes
* add posargs to tox config ([9618ce1](https://gitlab.developers.cam.ac.uk/uis/devops/iam/activate-account/api/commit/9618ce15e57f754edc0a2c3362814a0ee9e42be8))
## [0.3.1](https://gitlab.developers.cam.ac.uk/uis/devops/iam/activate-account/api/compare/0.3.0...0.3.1) (2024-10-30)
### Bug Fixes
......
# Generated by Django 4.2.14 on 2024-11-01 13:00
import django.db.models.functions.text
from django.db import migrations, models
import activate_account.models
class Migration(migrations.Migration):
dependencies = [
("activate_account", "0003_accountdetails_terms_accepted"),
]
operations = [
migrations.AlterField(
model_name="account",
name="crsid",
field=activate_account.models.CRSIdField(
max_length=20, primary_key=True, serialize=False
),
),
migrations.AddConstraint(
model_name="account",
constraint=models.UniqueConstraint(
django.db.models.functions.text.Lower("crsid"), name="unique_lowercase_crsid"
),
),
]
from django.db import models
from django.db.models.functions import Lower
class CRSIdField(models.CharField):
"""
The CRSIdField class is a character field subclass which enforces lower case storage in the
database. It also compares case insensitively, e.g. abc123 == ABC123 == aBc123.
"""
def get_prep_value(self, value):
value = super().get_prep_value(value)
return value if value is None else value.lower()
class Account(models.Model):
crsid = models.CharField(primary_key=True, max_length=20)
crsid = CRSIdField(primary_key=True, max_length=20)
last_name = models.CharField(max_length=100, db_collation="case_insensitive")
date_of_birth = models.DateField()
code = models.CharField(max_length=100)
......@@ -10,6 +22,7 @@ class Account(models.Model):
class Meta:
constraints = [
models.UniqueConstraint("last_name", "date_of_birth", "code", name="unique_account"),
models.UniqueConstraint(Lower("crsid"), name="unique_lowercase_crsid"),
]
......
......@@ -40,3 +40,14 @@ def test_account_separate_uniqueness():
AccountFactory()
assert Account.objects.count() == n_accounts
def test_account_crsid_case():
factory_account = AccountFactory(crsid="ABC123")
account = Account.objects.get(pk="abc123")
factory_account.refresh_from_db()
assert account == factory_account
# Check that creating a CRSId with different casing raises an integrity error
with pytest.raises(IntegrityError):
factory_account = AccountFactory(crsid="aBc123")
......@@ -13,7 +13,9 @@ def test_account_details(authenticated_api_client, account, account_details, url
assert response.status_code == status.HTTP_200_OK
assert response.data == {
"crsid": account.crsid,
# The `account` is returned from the factory, so the CRSId is still cased. The saved
# version in the database is always lowercased.
"crsid": account.crsid.lower(),
"last_name": account.last_name,
"date_of_birth": account.date_of_birth.strftime("%Y-%m-%d"),
"name": account_details.name,
......
......@@ -81,15 +81,16 @@ def test_valid_session_grant(client, valid_session_grant_body):
assert_is_valid_login(post_login(client, valid_session_grant_body))
@pytest.mark.parametrize("valid_session_grant_body", ["last_name"], indirect=True)
def test_valid_case_insensitive_last_name(client, valid_session_grant_body):
"""Last name comparison is case insensitive"""
@pytest.mark.parametrize("valid_session_grant_body", ["crsid", "last_name"], indirect=True)
def test_valid_case_insensitive(request, client, valid_session_grant_body):
"""CRSId and Last namecomparison is case insensitive"""
field = request.node.callspec.params["valid_session_grant_body"]
assert_is_valid_login(
post_login(
client,
{
**valid_session_grant_body,
"last_name": valid_session_grant_body["last_name"].upper(),
field: valid_session_grant_body[field].upper(),
},
)
)
......
[tool.poetry]
name = "activate_account"
version = "0.3.1"
version = "0.4.0"
description = ""
authors = [ ]
readme = "README.md"
......
......@@ -55,7 +55,7 @@ allowlist_externals=
commands_pre=
poetry install --no-root --sync
commands=
coverage run --source={toxinidir} -m pytest --junitxml={[_vars]build_root}/junit.xml
coverage run --source={toxinidir} -m pytest --junitxml={[_vars]build_root}/junit.xml {posargs}
coverage html --directory {[_vars]build_root}/htmlcov/
coverage report
coverage xml -o {env:COVERAGE_XML_FILE:{[_vars]build_root}/coverage.xml}
......