"""Unit tests for EmployeesTools.""" import pytest from unittest.mock import AsyncMock, MagicMock from agent_service.tools.employees_tools import EmployeesTools from agent_service.tools.odoo_client import WriteResult def _make_tools(): odoo = MagicMock() odoo.search_read = AsyncMock(return_value=[]) odoo.write = AsyncMock(return_value=WriteResult( success=True, model='', record_id=None, action='write')) odoo.create = AsyncMock(return_value=WriteResult( success=True, model='', record_id=42, action='create')) odoo.call = AsyncMock(return_value=True) return EmployeesTools(odoo) # ── get_employees ──────────────────────────────────────────────────────────── @pytest.mark.asyncio async def test_get_employees_default(): t = _make_tools() result = await t.get_employees() t._o.search_read.assert_awaited_once() domain = t._o.search_read.call_args[0][1] assert ('active', '=', True) in domain assert isinstance(result, list) @pytest.mark.asyncio async def test_get_employees_with_department(): t = _make_tools() await t.get_employees(department_id=5) domain = t._o.search_read.call_args[0][1] assert ('department_id', '=', 5) in domain @pytest.mark.asyncio async def test_get_employees_inactive(): t = _make_tools() await t.get_employees(active=False) domain = t._o.search_read.call_args[0][1] assert ('active', '=', False) in domain # ── get_employee_profile ────────────────────────────────────────────────────── @pytest.mark.asyncio async def test_get_employee_profile_found(): t = _make_tools() t._o.search_read = AsyncMock(return_value=[ {'id': 1, 'name': 'Alice', 'job_title': 'Dev', 'work_email': 'alice@co.com'} ]) result = await t.get_employee_profile(employee_id=1) assert result['name'] == 'Alice' domain = t._o.search_read.call_args[0][1] assert ('id', '=', 1) in domain @pytest.mark.asyncio async def test_get_employee_profile_not_found(): t = _make_tools() t._o.search_read = AsyncMock(return_value=[]) result = await t.get_employee_profile(employee_id=999) assert result == {} # ── get_leaves ──────────────────────────────────────────────────────────────── @pytest.mark.asyncio async def test_get_leaves_default(): t = _make_tools() result = await t.get_leaves() t._o.search_read.assert_awaited_once() assert isinstance(result, list) @pytest.mark.asyncio async def test_get_leaves_with_employee(): t = _make_tools() await t.get_leaves(employee_id=3) domain = t._o.search_read.call_args[0][1] assert ('employee_id', '=', 3) in domain @pytest.mark.asyncio async def test_get_leaves_with_state(): t = _make_tools() await t.get_leaves(state='validate') domain = t._o.search_read.call_args[0][1] assert ('state', '=', 'validate') in domain @pytest.mark.asyncio async def test_get_leaves_with_date_from(): t = _make_tools() await t.get_leaves(date_from='2026-01-01') domain = t._o.search_read.call_args[0][1] assert ('date_from', '>=', '2026-01-01') in domain # ── get_contracts ───────────────────────────────────────────────────────────── @pytest.mark.asyncio async def test_get_contracts_default_state_open(): t = _make_tools() await t.get_contracts() domain = t._o.search_read.call_args[0][1] assert ('state', '=', 'open') in domain @pytest.mark.asyncio async def test_get_contracts_with_employee(): t = _make_tools() await t.get_contracts(employee_id=7) domain = t._o.search_read.call_args[0][1] assert ('employee_id', '=', 7) in domain @pytest.mark.asyncio async def test_get_contracts_returns_list(): t = _make_tools() t._o.search_read = AsyncMock(return_value=[ {'id': 1, 'name': 'Contract A', 'wage': 50000.0} ]) result = await t.get_contracts() assert len(result) == 1 # ── get_attendance_summary ──────────────────────────────────────────────────── @pytest.mark.asyncio async def test_get_attendance_summary_totals(): t = _make_tools() t._o.search_read = AsyncMock(return_value=[ {'worked_hours': 8.0, 'check_in': '2026-01-05 09:00:00'}, {'worked_hours': 7.5, 'check_in': '2026-01-06 09:00:00'}, ]) result = await t.get_attendance_summary(employee_id=1, date_from='2026-01-01', date_to='2026-01-31') assert result['total_hours'] == 15.5 assert result['days_present'] == 2 assert result['attendance_records'] == 2 @pytest.mark.asyncio async def test_get_attendance_summary_structure(): t = _make_tools() result = await t.get_attendance_summary(employee_id=1, date_from='2026-01-01', date_to='2026-01-31') assert 'total_hours' in result assert 'days_present' in result assert result['employee_id'] == 1 # ── get_department_summary ──────────────────────────────────────────────────── @pytest.mark.asyncio async def test_get_department_summary_headcount(): t = _make_tools() t._o.search_read = AsyncMock(side_effect=[ [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}], # employees [{'id': 10, 'employee_id': [1, 'Alice'], 'wage': 60000.0}, {'id': 11, 'employee_id': [2, 'Bob'], 'wage': 55000.0}], # contracts ]) result = await t.get_department_summary(department_id=3) assert result['headcount'] == 2 assert result['department_id'] == 3 @pytest.mark.asyncio async def test_get_department_summary_avg_wage(): t = _make_tools() t._o.search_read = AsyncMock(side_effect=[ [{'id': 1, 'name': 'Alice'}], [{'id': 10, 'employee_id': [1, 'Alice'], 'wage': 60000.0}], ]) result = await t.get_department_summary(department_id=3) assert result['avg_wage'] == 60000.0 # ── flag_for_review ─────────────────────────────────────────────────────────── @pytest.mark.asyncio async def test_flag_for_review(): t = _make_tools() result = await t.flag_for_review('hr.employee', 1, 'Contract expired', severity='high') assert result is True call_args = t._o.call.call_args[0] assert call_args[1] == 'message_post' assert '[AI FLAG - HIGH]' in call_args[3]['body'] # ── post_chatter_note ───────────────────────────────────────────────────────── @pytest.mark.asyncio async def test_post_chatter_note(): t = _make_tools() result = await t.post_chatter_note('hr.employee', 1, 'Note text') assert result is True t._o.call.assert_awaited_once() call_args = t._o.call.call_args[0] assert call_args[3]['body'] == 'Note text'