feat(fusion_login_audit): add fusion.login.audit model
- All 16 columns per spec (user, attempted_login, result, failure_reason, event_time, ip/geo fields, user_agent triple, device_type, database). - Check constraint binds failure_reason presence to result value. - Three composite indexes (user+time, login+time, geo_state+time) supporting the per-user, failure-burst, and geo cron queries. - Minimal admin-read ACL added so subsequent tests can verify writes. - 3 TransactionCase tests passing: model create, failure_reason nullable on success, geo_lookup_state='internal' accepted. Odoo 19 deprecation note: this implementation uses the declarative models.Constraint and models.Index attributes (Odoo 19 silently drops the legacy `_sql_constraints = [...]` list and `init()`/raw-SQL pattern with only a warning). Captured in CLAUDE.md rule #9. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2
fusion_login_audit/tests/__init__.py
Normal file
2
fusion_login_audit/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import test_login_audit
|
||||
43
fusion_login_audit/tests/test_login_audit.py
Normal file
43
fusion_login_audit/tests/test_login_audit.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo.tests.common import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestFusionLoginAuditModel(TransactionCase):
|
||||
|
||||
def test_model_exists_and_creates(self):
|
||||
"""Audit row can be created with all expected fields."""
|
||||
Audit = self.env['fusion.login.audit'].sudo()
|
||||
rec = Audit.create({
|
||||
'attempted_login': 'demo@example.com',
|
||||
'result': 'success',
|
||||
'ip_address': '203.0.113.5',
|
||||
'user_agent_raw': 'Mozilla/5.0 Test',
|
||||
'browser': 'Test 1.0',
|
||||
'os': 'TestOS',
|
||||
'device_type': 'desktop',
|
||||
'database': self.env.cr.dbname,
|
||||
'geo_lookup_state': 'pending',
|
||||
})
|
||||
self.assertTrue(rec.id)
|
||||
self.assertEqual(rec.result, 'success')
|
||||
self.assertEqual(rec.geo_lookup_state, 'pending')
|
||||
self.assertEqual(rec.database, self.env.cr.dbname)
|
||||
self.assertTrue(rec.event_time) # default fires
|
||||
|
||||
def test_failure_reason_optional(self):
|
||||
"""failure_reason is null on success rows."""
|
||||
rec = self.env['fusion.login.audit'].sudo().create({
|
||||
'attempted_login': 'demo@example.com',
|
||||
'result': 'success',
|
||||
})
|
||||
self.assertFalse(rec.failure_reason)
|
||||
|
||||
def test_geo_state_internal_value(self):
|
||||
"""`internal` is an accepted geo_lookup_state value (distinct from private_ip)."""
|
||||
rec = self.env['fusion.login.audit'].sudo().create({
|
||||
'attempted_login': 'demo@example.com',
|
||||
'result': 'success',
|
||||
'geo_lookup_state': 'internal',
|
||||
})
|
||||
self.assertEqual(rec.geo_lookup_state, 'internal')
|
||||
Reference in New Issue
Block a user