Skip to content

Commit 2c29415

Browse files
committed
adding tests
1 parent 3304884 commit 2c29415

11 files changed

Lines changed: 242 additions & 2 deletions

File tree

.github/workflows/django-tests.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Config file for GitHub Actions
2+
3+
name: Django CI
4+
5+
on: [push, pull_request]
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest # Fastest option
10+
11+
steps:
12+
- name: Checkout code
13+
uses: actions/checkout@v6 # Copies code on VM
14+
15+
- name: Set up Python
16+
uses: actions/setup-python@v5 # Official GitHub tool
17+
with:
18+
python-version: '3.12'
19+
20+
- name: Install dependencies
21+
run: | # Everything below will be treated as one line
22+
python -m pip install --upgrade pip
23+
pip install -r requirements.txt
24+
25+
- name: Run migrations
26+
env: # Create env at virtual machine
27+
DJANGO_SETTINGS_MODULE: backend.config.settings # Show where is the brain of the project
28+
run: |
29+
python manage.py migrate
30+
31+
32+
- name: Run tests
33+
env:
34+
DJANGO_SETTINGS_MODULE: backend.config.settings
35+
PYTHONPATH: ${{ github.workspace }} # github.workspace is a root dir, safe config
36+
run: |
37+
pytest --cov=backend/apps --cov-report=term-missing

backend/apps/conftest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import pytest
2+
from model_bakery import baker
3+
4+
5+
@pytest.fixture
6+
def user(db): # db needed to open connection with db
7+
return baker.make("auth.User", username="Test_user")
8+
9+
10+
@pytest.fixture
11+
def post(db, user):
12+
return baker.make("posts.Post", title="Testing", author=user)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Generated by Django 6.0 on 2026-02-09 19:44
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('interactions', '0005_alter_comment_id_alter_follow_id_alter_like_id'),
11+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12+
]
13+
14+
operations = [
15+
migrations.AlterField(
16+
model_name='comment',
17+
name='id',
18+
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
19+
),
20+
migrations.AlterField(
21+
model_name='follow',
22+
name='id',
23+
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
24+
),
25+
migrations.AlterField(
26+
model_name='like',
27+
name='id',
28+
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
29+
),
30+
migrations.AddConstraint(
31+
model_name='follow',
32+
constraint=models.CheckConstraint(condition=models.Q(('follower', models.F('following')), _negated=True), name='prevent_self_follow'),
33+
),
34+
]

backend/apps/interactions/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ class Follow(models.Model):
3434
class Meta:
3535
unique_together = ("follower", "following")
3636
ordering = ["-created_at"]
37+
# Constraint to prevent self follow on database level
38+
constraints = [
39+
models.CheckConstraint(
40+
# Object needed to negate it with ~, constraint doesn't accept other symbols than =
41+
condition=~models.Q(follower=models.F("following")), # models.F checks value in column 'following' for the same row
42+
name="prevent_self_follow"
43+
)
44+
]
3745

3846
def __str__(self):
3947
return f"{self.follower.username} follows {self.following.username}"

backend/apps/interactions/tests.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,65 @@
1-
from django.test import TestCase
1+
import pytest
2+
from model_bakery import baker
3+
from datetime import datetime
4+
from backend.apps.interactions.models import Like, Follow
5+
from django.db import IntegrityError
26

37
# Create your tests here.
8+
9+
10+
@pytest.mark.django_db(transaction=True) # Lets Django clean up database after error to continue assertions, no TransactionManagementError
11+
def test_like_creation_and_duplicate(user, post):
12+
"""
13+
Check is like is created correctly,
14+
error when duplicates
15+
"""
16+
like = baker.make("interactions.Like", user=user, post=post)
17+
18+
assert Like.objects.count() == 1 # Manager doesn't exist for instance
19+
assert like.post == post
20+
assert like.user == user
21+
assert isinstance(like.created_at, datetime)
22+
23+
with pytest.raises(IntegrityError):
24+
baker.make("interactions.Like", user=user, post=post) # We want this error, values must be unique
25+
26+
assert Like.objects.count() == 1 # To make sure there is still only one object
27+
28+
29+
@pytest.mark.django_db
30+
def test_comment_creation(user, post):
31+
"""
32+
Checks if comment is created correctly
33+
"""
34+
35+
comment = baker.make("interactions.Comment", user=user, post=post)
36+
37+
assert comment.user == user
38+
assert comment.post == post
39+
assert comment.body is not None
40+
assert isinstance(comment.created_at, datetime)
41+
42+
43+
@pytest.mark.django_db(transaction=True)
44+
def test_follow_creation(user):
45+
"""
46+
Checks if follow is created correctly
47+
Error if follow duplicates
48+
Error if users tries to follow himself
49+
"""
50+
user2 = baker.make("auth.User")
51+
follow = baker.make("interactions.Follow", follower=user, following=user2)
52+
assert Follow.objects.count() == 1
53+
assert follow.follower == user
54+
assert follow.following == user2
55+
assert isinstance(follow.created_at, datetime)
56+
57+
# Calling error, duplicate
58+
with pytest.raises(IntegrityError):
59+
baker.make("interactions.Follow", follower=user, following=user2)
60+
61+
# Calling error, self follow
62+
with pytest.raises(IntegrityError):
63+
baker.make("interactions.Follow", follower=user, following=user)
64+
65+
assert Follow.objects.count() == 1
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 6.0 on 2026-02-09 19:44
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('posts', '0004_alter_post_id'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='post',
15+
name='id',
16+
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
17+
),
18+
]

backend/apps/posts/tests.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
11
from django.test import TestCase
2+
import pytest
3+
from model_bakery import baker
4+
from datetime import datetime
25

36
# Create your tests here.
7+
8+
9+
@pytest.mark.django_db
10+
def test_post_creation(user, post):
11+
"""
12+
Check is post is created correctly
13+
"""
14+
15+
assert post.title == "Testing"
16+
assert post.body is not None
17+
assert post.author == user
18+
assert isinstance(post.date, datetime)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 6.0 on 2026-02-09 19:44
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('users', '0002_alter_profile_id_alter_profile_user'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='profile',
15+
name='id',
16+
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
17+
),
18+
]

backend/apps/users/tests.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1-
from django.test import TestCase
1+
import pytest
2+
from model_bakery import baker
23

34
# Create your tests here.
5+
6+
7+
@pytest.mark.django_db
8+
def test_user_profile_creation():
9+
"""
10+
Check is user is created correctly
11+
"""
12+
user = baker.make('auth.User', username="Test")
13+
14+
assert user.username == "Test"
15+
assert user.is_active is True
16+
assert user.id is not None

pytest.ini

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[pytest]
2+
3+
# Settings path
4+
DJANGO_SETTINGS_MODULE = backend.config.settings
5+
6+
# For apps imports
7+
pythonpath = . backend
8+
9+
# Where to look for tests
10+
testpaths = backend/apps
11+
12+
# Test filenames
13+
python_files = tests.py test_*.py *_tests.py
14+
15+
# Django will tru to reuse db instead of creating one, no migrations issues
16+
addopts = -v --tb=short --reuse-db
17+
18+
markers:
19+
unit: logic tests
20+
view: template and view test

0 commit comments

Comments
 (0)