Skip to content

[WIP] Add test coverage #782

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: 17.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions spp_attendance/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import test_attendance_api_client
from . import test_attendance_list
from . import test_attendance_subscriber
from . import test_attendance_controllers
78 changes: 78 additions & 0 deletions spp_attendance/tests/test_attendance_api_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from datetime import datetime, timedelta
from unittest.mock import patch

from odoo.exceptions import UserError
from odoo.tests import TransactionCase


class TestAttendanceApiClient(TransactionCase):
def setUp(self):
super().setUp()
self.client = self.env["spp.attendance.api.client.credential"].create({"name": "Test Client"})

def test_generate_client_id(self):
"""Test client ID generation is unique and follows format"""
client_id = self.client.client_id
self.assertTrue(client_id.startswith("c-id-"))
self.assertEqual(len(client_id), 41) # c-id- + 36 chars UUID

# Test uniqueness
client2 = self.env["spp.attendance.api.client.credential"].create({"name": "Test Client 2"})
self.assertNotEqual(client_id, client2.client_id)

def test_generate_client_secret(self):
"""Test client secret generation is unique and follows format"""
client_secret = self.client.client_secret
self.assertTrue(client_secret.startswith("c-secret-"))
self.assertEqual(len(client_secret), 45) # c-secret- + 36 chars UUID

# Test uniqueness
client2 = self.env["spp.attendance.api.client.credential"].create({"name": "Test Client 2"})
self.assertNotEqual(client_secret, client2.client_secret)

@patch("jwt.encode")
@patch("odoo.addons.spp_oauth.tools.rsa_encode_decode.get_private_key")
def test_generate_access_token(self, mock_get_private_key, mock_jwt_encode):
"""Test access token generation"""
mock_get_private_key.return_value = "test_private_key"
mock_jwt_encode.return_value = "test_token"
token = self.client.generate_access_token()
self.assertEqual(token, "test_token")
self.assertTrue(mock_jwt_encode.called)

def test_show_credentials(self):
"""Test showing credentials works only once"""
# First time should work
action = self.client.show_credentials()
self.assertEqual(action["type"], "ir.actions.act_window")
self.assertTrue(self.client.show_button_clicked)

# Second time should raise error
with self.assertRaises(UserError):
self.client.show_credentials()

def test_export_data_restriction(self):
"""Test export data is restricted"""
with self.assertRaises(UserError):
self.client.export_data(["name", "client_id"])

@patch("jwt.encode")
@patch("odoo.addons.spp_oauth.tools.rsa_encode_decode.get_private_key")
@patch("odoo.addons.spp_attendance.models.attendance_api_client_credentials.TOKEN_EXPIRATION_MIN", 10)
def test_token_expiration(self, mock_get_private_key, mock_jwt_encode):
"""Test token expiration time"""
mock_get_private_key.return_value = "test_private_key"
mock_jwt_encode.side_effect = ["token1", "token2"]
now = datetime.now()
with patch("odoo.addons.spp_attendance.models.attendance_api_client_credentials.datetime") as mock_datetime:
mock_datetime.today.return_value = now
mock_datetime.strptime = datetime.strptime
mock_datetime.now.return_value = now

token = self.client.generate_access_token()
self.assertEqual(token, "token1")

# Token should be valid for TOKEN_EXPIRATION_MIN minutes
mock_datetime.today.return_value = now + timedelta(minutes=9)
token2 = self.client.generate_access_token()
self.assertEqual(token2, "token2")
163 changes: 163 additions & 0 deletions spp_attendance/tests/test_attendance_controllers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import json
from datetime import datetime
from unittest.mock import patch

from odoo.tests import HttpCase, tagged


@tagged("-at_install", "post_install")
class TestAttendanceControllers(HttpCase):
def setUp(self):
super().setUp()
# Create test data
self.client = self.env["spp.attendance.api.client.credential"].create({"name": "Test Client"})
self.type = self.env["spp.attendance.type"].create({"name": "Test Type"})
self.location = self.env["spp.attendance.location"].create({"name": "Test Location"})

# Create test subscriber upfront
self.subscriber = self.env["spp.attendance.subscriber"].create(
{"family_name": "Test", "given_name": "Subscriber", "person_identifier": "TEST123"}
)

@patch("jwt.encode")
@patch("odoo.addons.spp_oauth.tools.rsa_encode_decode.get_private_key")
def test_auth_token(self, mock_get_private_key, mock_jwt_encode):
"""Test authentication token endpoint"""
mock_get_private_key.return_value = "test_private_key"
mock_jwt_encode.return_value = "test_token"

data = {
"client_id": self.client.client_id,
"client_secret": self.client.client_secret,
}

# Add error handling for response
try:
response = self.url_open(
"/auth/token",
data=json.dumps(data),
headers={"Content-Type": "application/json"},
)
self.assertEqual(response.status_code, 200)
result = json.loads(response.content)
self.assertEqual(result.get("access_token"), "test_token")
self.assertEqual(result.get("token_type"), "Bearer")
except Exception as e:
self.fail(f"Request failed: {str(e)}")

Check warning on line 46 in spp_attendance/tests/test_attendance_controllers.py

View check run for this annotation

Codecov / codecov/patch

spp_attendance/tests/test_attendance_controllers.py#L45-L46

Added lines #L45 - L46 were not covered by tests

@patch("jwt.encode")
@patch("odoo.addons.spp_oauth.tools.rsa_encode_decode.get_private_key")
@patch("odoo.addons.spp_oauth.tools.rsa_encode_decode.get_public_key")
@patch("jwt.decode")
def test_create_attendance(self, mock_jwt_decode, mock_get_public_key, mock_get_private_key, mock_jwt_encode):
"""Test attendance creation endpoint"""
# Setup mocks
mock_get_private_key.return_value = "test_private_key"
mock_get_public_key.return_value = "test_public_key"
mock_jwt_encode.return_value = "test_token"
mock_jwt_decode.return_value = {"iss": "openspp:auth-service"}

# Get auth token
auth_data = {
"client_id": self.client.client_id,
"client_secret": self.client.client_secret,
}

try:
auth_response = self.url_open(
"/auth/token",
data=json.dumps(auth_data),
headers={"Content-Type": "application/json"},
)
token = json.loads(auth_response.content)["access_token"]

# Create attendance with existing subscriber
attendance_data = {
"records": [
{
"person_id": self.subscriber.person_identifier,
"time_card": [
{
"date_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"attendance_type": self.type.id,
"attendance_location": self.location.id,
"attendance_category": "present",
}
],
}
],
"submitted_by": "Test User",
"submitted_datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}

response = self.url_open(
"/attendances",
data=json.dumps(attendance_data),
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
},
)

self.assertEqual(response.status_code, 200)
result = json.loads(response.content)
self.assertEqual(result["message"], "Attendance list created successfully.")
self.assertEqual(result["person_ids"], [self.subscriber.person_identifier])

except Exception as e:
self.fail(f"Request failed: {str(e)}")

Check warning on line 108 in spp_attendance/tests/test_attendance_controllers.py

View check run for this annotation

Codecov / codecov/patch

spp_attendance/tests/test_attendance_controllers.py#L107-L108

Added lines #L107 - L108 were not covered by tests

@patch("jwt.encode")
@patch("odoo.addons.spp_oauth.tools.rsa_encode_decode.get_private_key")
@patch("odoo.addons.spp_oauth.tools.rsa_encode_decode.get_public_key")
@patch("jwt.decode")
def test_get_attendance(self, mock_jwt_decode, mock_get_public_key, mock_get_private_key, mock_jwt_encode):
"""Test attendance retrieval endpoint"""
mock_get_private_key.return_value = "test_private_key"
mock_get_public_key.return_value = "test_public_key"
mock_jwt_encode.return_value = "test_token"
mock_jwt_decode.return_value = {"iss": "openspp:auth-service"}

try:
# Setup auth
auth_data = {
"client_id": self.client.client_id,
"client_secret": self.client.client_secret,
}
auth_response = self.url_open(
"/auth/token",
data=json.dumps(auth_data),
headers={"Content-Type": "application/json"},
)
token = json.loads(auth_response.content)["access_token"]

# Create attendance record for existing subscriber
self.env["spp.attendance.list"].create(
{
"subscriber_id": self.subscriber.id,
"attendance_date": datetime.now().strftime("%Y-%m-%d"),
"attendance_time": "10:00:00",
"attendance_type_id": self.type.id,
"attendance_location_id": self.location.id,
"attendance_category": "present",
"submitted_by": "Test User",
"submitted_datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}
)

# Test get attendance
response = self.url_open(
f"/attendance/{self.subscriber.person_identifier}",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
},
)

self.assertEqual(response.status_code, 200)
result = json.loads(response.content)
self.assertEqual(result["person_id"], self.subscriber.person_identifier)
self.assertEqual(len(result["attendance_list"]), 1)

except Exception as e:
self.fail(f"Request failed: {str(e)}")

Check warning on line 163 in spp_attendance/tests/test_attendance_controllers.py

View check run for this annotation

Codecov / codecov/patch

spp_attendance/tests/test_attendance_controllers.py#L162-L163

Added lines #L162 - L163 were not covered by tests
115 changes: 115 additions & 0 deletions spp_attendance/tests/test_attendance_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from odoo.tests import TransactionCase


class TestAttendanceList(TransactionCase):
def setUp(self):
super().setUp()
self.partner = self.env["res.partner"].create(
{
"name": "Test Partner",
"family_name": "Test",
"given_name": "Partner",
"identifier": "TEST123",
}
)
self.subscriber = self.env["spp.attendance.subscriber"].create(
{
"partner_id": self.partner.id,
"person_identifier": "TEST123",
}
)
self.type = self.env["spp.attendance.type"].create(
{
"name": "Test Type",
"description": "Test Description",
}
)
self.location = self.env["spp.attendance.location"].create(
{
"name": "Test Location",
"description": "Test Description",
}
)

def test_attendance_creation(self):
"""Test basic attendance creation"""
attendance = self.env["spp.attendance.list"].create(
{
"subscriber_id": self.subscriber.id,
"attendance_date": "2024-03-20",
"attendance_time": "10:00:00",
"attendance_type_id": self.type.id,
"attendance_location_id": self.location.id,
"attendance_description": "Test attendance",
"attendance_category": "present",
"submitted_by": "Test User",
"submitted_datetime": "2024-03-20 10:00:00",
}
)

self.assertEqual(attendance.subscriber_id, self.subscriber)
self.assertEqual(attendance.attendance_date.strftime("%Y-%m-%d"), "2024-03-20")
self.assertEqual(attendance.attendance_time, "10:00:00")
self.assertEqual(attendance.attendance_type_id, self.type)
self.assertEqual(attendance.attendance_location_id, self.location)
self.assertEqual(attendance.attendance_category, "present")

def test_attendance_uniqueness(self):
"""Test attendance uniqueness constraints"""
# Enable all uniqueness constraints
self.env["ir.config_parameter"].sudo().set_param("spp_attendance.date_unique", True)
self.env["ir.config_parameter"].sudo().set_param("spp_attendance.time_unique", True)
self.env["ir.config_parameter"].sudo().set_param("spp_attendance.type_unique", True)
self.env["ir.config_parameter"].sudo().set_param("spp_attendance.location_unique", True)

# Create first attendance
attendance1 = self.env["spp.attendance.list"].create(
{
"subscriber_id": self.subscriber.id,
"attendance_date": "2024-03-20",
"attendance_time": "10:00:00",
"attendance_type_id": self.type.id,
"attendance_location_id": self.location.id,
"attendance_category": "present",
"submitted_by": "Test User",
"submitted_datetime": "2024-03-20 10:00:00",
}
)

# Try to create duplicate attendance
attendance2 = self.env["spp.attendance.list"].new(
{
"subscriber_id": self.subscriber.id,
"attendance_date": attendance1.attendance_date, # Use same date as attendance1
"attendance_time": attendance1.attendance_time, # Use same time as attendance1
"attendance_type_id": attendance1.attendance_type_id.id, # Use same type as attendance1
"attendance_location_id": attendance1.attendance_location_id.id, # Use same location as attendance1
"attendance_category": "present",
"submitted_by": "Test User",
"submitted_datetime": "2024-03-20 10:00:00",
}
)

# Check uniqueness should return False since this would be a duplicate
self.assertFalse(attendance2.check_uniqueness())

# Verify that changing any of the unique fields allows creation
attendance2.attendance_time = "11:00:00" # Change time
self.assertTrue(attendance2.check_uniqueness())

def test_attendance_categories(self):
"""Test attendance categories"""
attendance = self.env["spp.attendance.list"].create(
{
"subscriber_id": self.subscriber.id,
"attendance_date": "2024-03-20",
"attendance_time": "10:00:00",
"attendance_category": "present",
"submitted_by": "Test User",
"submitted_datetime": "2024-03-20 10:00:00",
}
)
self.assertEqual(attendance.attendance_category, "present")

attendance.attendance_category = "absent"
self.assertEqual(attendance.attendance_category, "absent")
Loading
Loading