Skip to content

Commit

Permalink
test: add django unitest (#202)
Browse files Browse the repository at this point in the history
* test(login,mockapi): Add tests, modify database models and update dependencies

In this commit, unit tests for mockapi serializers and login views are added to ensure proper functionality. Database model changes include addition of new columns and alteration of existing columns in 'MockAPI' model. Project dependencies are also updated with the addition of pytest. Last but not the least, login views have been updated for returning appropriate HTTP status codes and URL configurations updated.

* ci: Add GitHub Actions workflow and alter Django model

A GitHub Actions workflow for running pytest and coverage has been added to the project. In addition, a modification to the Django model has been made to alter the "first_name" field for the "MyUser" model.

---------

Co-authored-by: rikasai233 <[email protected]>
  • Loading branch information
lihuacai168 and trumpchifan authored Mar 14, 2024
1 parent 8d26f03 commit 70f15e5
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 11 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/django_unittest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Python application test with pytest and coverage
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize]
paths:
- '**.py'
- 'requirements.txt'
- '!./web/**'
- 'Dockerfile'
jobs:
build:
runs-on: ubuntu-latest
steps:

- name: Check out code
uses: actions/checkout@v2
- name: Set up Python 3.11
uses: actions/setup-python@v2
with:
python-version: 3.11

- name: Install poetry
run: |
curl -sSL https://install.python-poetry.org | python3 -
- name: Install dependencies
run: |
python -m pip install --upgrade pip
poetry install
- name: Test with pytest and coverage
run: |
pytest tests/ --cov=tests/
- name: Upload coverage to Codecove
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true
18 changes: 18 additions & 0 deletions fastrunner/migrations/0024_project_is_deleted.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.1.13 on 2024-03-15 01:11

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("fastrunner", "0023_auto_20210910_1155"),
]

operations = [
migrations.AddField(
model_name="project",
name="is_deleted",
field=models.IntegerField(default=0, null=True, verbose_name="是否删除"),
),
]
20 changes: 20 additions & 0 deletions fastuser/migrations/0007_alter_myuser_first_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.1.13 on 2024-03-15 01:10

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("fastuser", "0006_myuser_show_hosts"),
]

operations = [
migrations.AlterField(
model_name="myuser",
name="first_name",
field=models.CharField(
blank=True, max_length=150, verbose_name="first name"
),
),
]
2 changes: 1 addition & 1 deletion fastuser/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
urlpatterns = [
# 关闭注册入口,改为django admin创建用户
# path('register/', views.RegisterView.as_view()),
path("login/", views.LoginView.as_view()),
path("login/", views.LoginView.as_view(), name="login"),
path("list/", views.UserView.as_view()),
path("auto_run_testsuite_pk/", timer_task.auto_run_testsuite_pk, name="auto_run_testsuite_pk"),
]
2 changes: 1 addition & 1 deletion fastuser/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def post(self, request):
logger.info(f"Authentication failed for {username=}")
return Response(response.LOGIN_FAILED)
else:
return Response(serializer.errors)
return Response(serializer.errors, status=400)


class UserView(APIView):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Generated by Django 4.1.13 on 2024-03-15 01:18

from django.db import migrations, models
import mock.models


class Migration(migrations.Migration):

dependencies = [
("mock", "0005_mockapilog_request_id_alter_mockapi_api_id_and_more"),
]

operations = [
migrations.AddField(
model_name="mockapi",
name="request_body",
field=models.JSONField(blank=True, default=dict, null=True),
),
migrations.AddField(
model_name="mockapi",
name="version",
field=models.IntegerField(default=1),
),
migrations.AlterField(
model_name="mockapi",
name="api_id",
field=models.CharField(
default=mock.models.generate_uuid, max_length=32, unique=True
),
),
migrations.AlterField(
model_name="mockapi",
name="api_name",
field=models.CharField(max_length=100),
),
migrations.AlterField(
model_name="mockapi",
name="response_text",
field=models.TextField(
default='\ndef execute(req, resp):\n import requests\n\n url = "http://localhost:8000/api/mock/mock_api/"\n\n payload = {}\n headers = {\n "accept": "application/json",\n "X-CSRFToken": "fk5wQDlKC6ufRjk7r38pfbqyq7mTtyc5NUUqkFN5lbZf6nyHVSbAUVoqbwaGcQHT",\n }\n\n response = requests.request("GET", url, headers=headers, data=payload)\n resp.data = response.json()\n'
),
),
migrations.AlterField(
model_name="mockapilog",
name="request_id",
field=models.CharField(
blank=True,
db_index=True,
default=mock.models.generate_uuid,
max_length=100,
null=True,
),
),
migrations.AlterField(
model_name="mockproject",
name="project_id",
field=models.CharField(
default=mock.models.generate_uuid, max_length=100, unique=True
),
),
]
26 changes: 19 additions & 7 deletions mock/models.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import uuid

from django.db import models

from fastuser.models import BaseTable


def generate_uuid():
return uuid.uuid4().hex


class MockProject(BaseTable):
project_id = models.CharField(max_length=100, unique=True, default=lambda: uuid.uuid4().hex)
project_id = models.CharField(max_length=100, unique=True, default=generate_uuid)
project_name = models.CharField(max_length=100)
project_desc = models.CharField(max_length=100)
is_active = models.BooleanField(default=True)
Expand Down Expand Up @@ -53,15 +56,17 @@ class MockAPI(BaseTable):
related_name="mock_apis",
)
request_path = models.CharField(max_length=100)
request_method = models.CharField(max_length=10, choices=METHOD_CHOICES, default="POST")
request_method = models.CharField(
max_length=10, choices=METHOD_CHOICES, default="POST"
)
request_body = models.JSONField(default=dict, blank=True, null=True)
response_text = models.TextField(default=resp_text)
is_active = models.BooleanField(default=True)

api_name = models.CharField(max_length=100)
api_desc = models.CharField(max_length=100, null=True, blank=True)
# uuid hex
api_id = models.CharField(max_length=32, default=lambda: uuid.uuid4().hex, unique=True)
api_id = models.CharField(max_length=32, default=generate_uuid, unique=True)
enabled = models.BooleanField(default=True)

# 添加version字段用于乐观锁控制
Expand All @@ -78,8 +83,13 @@ class Meta:


class MockAPILog(BaseTable):
api = models.ForeignKey(MockAPI, on_delete=models.DO_NOTHING, db_constraint=False, to_field='api_id',
related_name="logs")
api = models.ForeignKey(
MockAPI,
on_delete=models.DO_NOTHING,
db_constraint=False,
to_field="api_id",
related_name="logs",
)
project = models.ForeignKey(
MockProject,
on_delete=models.DO_NOTHING,
Expand All @@ -91,7 +101,9 @@ class MockAPILog(BaseTable):
)
request_obj = models.JSONField(default=dict, blank=True)
response_obj = models.JSONField(default=dict, null=True, blank=True)
request_id = models.CharField(max_length=100, default=lambda: uuid.uuid4().hex, db_index=True, null=True, blank=True)
request_id = models.CharField(
max_length=100, default=generate_uuid, db_index=True, null=True, blank=True
)

class Meta:
verbose_name = "mock api log表"
Expand Down
68 changes: 66 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ gevent = "22.10.2"
django-filter = ">=2.4.0,<2.5.0"
django-auth-ldap = "2.3.0"
pymysql = "1.1.0"
pytest = "^8.1.1"
pytest-django = "^4.8.0"


[tool.poetry.group.dev.dependencies]
Expand Down
53 changes: 53 additions & 0 deletions tests/test_login_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import pytest
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient
from django.contrib.auth import get_user_model

# 假设LoginView用的Url名字是'login'
login_url = reverse("login")


@pytest.mark.django_db # 如果你的测试需要数据库操作
class TestLoginView:
def setup_method(self):
User = get_user_model()
User.objects.create_user('validUser', '[email protected]', 'validPassword')

def test_login_with_correct_credentials(self):
client = APIClient()
user_data = {"username": "validUser", "password": "validPassword"}
response = client.post(login_url, user_data, format="json")
assert response.status_code == status.HTTP_200_OK
assert (
"token" in response.data
) # assuming that generate_token_and_respond returns a token in response

def test_login_with_incorrect_credentials(self):
client = APIClient()
user_data = {"username": "invalidUser", "password": "invalidPassword"}
response = client.post(login_url, user_data, format="json")
# body
assert response.json() == {'code': "0103", 'success': False, 'msg': "用户名或密码错误"}
assert (
response.status_code == status.HTTP_200_OK
) # assuming LOGIN_FAILED responds with status 401

def test_login_with_no_credentials(self):
client = APIClient()
user_data = {}
response = client.post(login_url, user_data, format="json")
assert response.status_code == status.HTTP_400_BAD_REQUEST

def test_login_with_partial_credentials(self):
client = APIClient()

# Missing username
user_data = {"password": "validPassword"}
response = client.post(login_url, user_data, format="json")
assert response.status_code == status.HTTP_400_BAD_REQUEST

# Missing password
user_data = {"username": "validUser"}
response = client.post(login_url, user_data, format="json")
assert response.status_code == status.HTTP_400_BAD_REQUEST
Loading

0 comments on commit 70f15e5

Please sign in to comment.