auto-claude: subtask-7-2 - Test collaboration matching
Created comprehensive test suite for IT audit collaboration matching: 1. Unit tests (tests/test_it_audit_collaboration.py): - 12 tests verifying all 6 match types - Backup replication, shared licensing, Teams federation - Shared monitoring, collective purchasing, knowledge sharing - Edge cases for size parsing and similarity 2. Integration test script (scripts/test_collaboration_matching.py): - Creates test audits with matching criteria - Runs collaboration matching algorithm - Verifies matches saved to database All unit tests pass (12/12). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ae1a62be31
commit
fa45b4b793
204
scripts/test_collaboration_matching.py
Normal file
204
scripts/test_collaboration_matching.py
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test Collaboration Matching Integration
|
||||||
|
========================================
|
||||||
|
|
||||||
|
This script tests the collaboration matching functionality by:
|
||||||
|
1. Creating two IT audits with matching criteria (Proxmox PBS + open_to_backup_replication)
|
||||||
|
2. Running the collaboration matching algorithm
|
||||||
|
3. Verifying matches appear in the database
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
# From the worktree directory with DEV database running:
|
||||||
|
DATABASE_URL=postgresql://nordabiz_app:NordaBiz2025Secure@localhost:5433/nordabiz \
|
||||||
|
python3 scripts/test_collaboration_matching.py
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- PostgreSQL DEV database running on localhost:5433
|
||||||
|
- IT audit tables created (run migration first)
|
||||||
|
|
||||||
|
Author: Norda Biznes Development Team
|
||||||
|
Created: 2026-01-09
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# Set database URL for DEV environment
|
||||||
|
if 'DATABASE_URL' not in os.environ:
|
||||||
|
os.environ['DATABASE_URL'] = 'postgresql://nordabiz_app:NordaBiz2025Secure@localhost:5433/nordabiz'
|
||||||
|
|
||||||
|
# Import after setting DATABASE_URL
|
||||||
|
from database import SessionLocal, Company, ITAudit, ITCollaborationMatch
|
||||||
|
from it_audit_service import ITAuditService
|
||||||
|
|
||||||
|
|
||||||
|
def get_test_companies(db, limit=2):
|
||||||
|
"""Get two test companies from the database"""
|
||||||
|
companies = db.query(Company).filter(
|
||||||
|
Company.status == 'active'
|
||||||
|
).limit(limit).all()
|
||||||
|
return companies
|
||||||
|
|
||||||
|
|
||||||
|
def create_test_audit(db, company_id: int, audit_data: dict) -> ITAudit:
|
||||||
|
"""Create a test IT audit for a company"""
|
||||||
|
service = ITAuditService(db)
|
||||||
|
return service.save_audit(company_id, audit_data)
|
||||||
|
|
||||||
|
|
||||||
|
def run_matching_test():
|
||||||
|
"""Run the collaboration matching test"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("IT Audit Collaboration Matching Test")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
# Step 1: Get two test companies
|
||||||
|
print("\n[1] Getting test companies...")
|
||||||
|
companies = get_test_companies(db, limit=2)
|
||||||
|
|
||||||
|
if len(companies) < 2:
|
||||||
|
print("ERROR: Need at least 2 active companies in database")
|
||||||
|
return False
|
||||||
|
|
||||||
|
company_a, company_b = companies[0], companies[1]
|
||||||
|
print(f" Company A: {company_a.name} (ID: {company_a.id})")
|
||||||
|
print(f" Company B: {company_b.name} (ID: {company_b.id})")
|
||||||
|
|
||||||
|
# Step 2: Check for existing audits and delete them for clean test
|
||||||
|
print("\n[2] Cleaning up existing test audits...")
|
||||||
|
db.query(ITAudit).filter(
|
||||||
|
ITAudit.company_id.in_([company_a.id, company_b.id])
|
||||||
|
).delete(synchronize_session='fetch')
|
||||||
|
|
||||||
|
db.query(ITCollaborationMatch).filter(
|
||||||
|
(ITCollaborationMatch.company_a_id.in_([company_a.id, company_b.id])) |
|
||||||
|
(ITCollaborationMatch.company_b_id.in_([company_a.id, company_b.id]))
|
||||||
|
).delete(synchronize_session='fetch')
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
print(" Existing audits and matches cleaned up.")
|
||||||
|
|
||||||
|
# Step 3: Create audit for Company A with PBS + open_to_backup_replication
|
||||||
|
print("\n[3] Creating IT audit for Company A with Proxmox PBS...")
|
||||||
|
audit_a_data = {
|
||||||
|
'has_proxmox_pbs': True,
|
||||||
|
'open_to_backup_replication': True,
|
||||||
|
'backup_solution': 'Proxmox Backup Server',
|
||||||
|
'backup_frequency': 'daily',
|
||||||
|
'has_edr': True,
|
||||||
|
'has_mfa': True,
|
||||||
|
'has_azure_ad': True,
|
||||||
|
'has_m365': True,
|
||||||
|
'open_to_shared_licensing': True,
|
||||||
|
'open_to_teams_federation': True,
|
||||||
|
'employee_count': '11-50',
|
||||||
|
'monitoring_solution': 'Zabbix',
|
||||||
|
'open_to_shared_monitoring': True,
|
||||||
|
}
|
||||||
|
audit_a = create_test_audit(db, company_a.id, audit_a_data)
|
||||||
|
print(f" Audit A created: ID={audit_a.id}, PBS={audit_a.has_proxmox_pbs}, "
|
||||||
|
f"Open to backup replication={audit_a.open_to_backup_replication}")
|
||||||
|
print(f" Scores: overall={audit_a.overall_score}, security={audit_a.security_score}, "
|
||||||
|
f"collaboration={audit_a.collaboration_score}")
|
||||||
|
|
||||||
|
# Step 4: Create audit for Company B with PBS + open_to_backup_replication
|
||||||
|
print("\n[4] Creating IT audit for Company B with Proxmox PBS...")
|
||||||
|
audit_b_data = {
|
||||||
|
'has_proxmox_pbs': True,
|
||||||
|
'open_to_backup_replication': True,
|
||||||
|
'backup_solution': 'Proxmox Backup Server',
|
||||||
|
'backup_frequency': 'daily',
|
||||||
|
'has_edr': True,
|
||||||
|
'has_mfa': True,
|
||||||
|
'has_azure_ad': True,
|
||||||
|
'has_m365': True,
|
||||||
|
'open_to_shared_licensing': True,
|
||||||
|
'open_to_teams_federation': True,
|
||||||
|
'employee_count': '51-100',
|
||||||
|
'monitoring_solution': 'Zabbix',
|
||||||
|
'open_to_shared_monitoring': True,
|
||||||
|
}
|
||||||
|
audit_b = create_test_audit(db, company_b.id, audit_b_data)
|
||||||
|
print(f" Audit B created: ID={audit_b.id}, PBS={audit_b.has_proxmox_pbs}, "
|
||||||
|
f"Open to backup replication={audit_b.open_to_backup_replication}")
|
||||||
|
print(f" Scores: overall={audit_b.overall_score}, security={audit_b.security_score}, "
|
||||||
|
f"collaboration={audit_b.collaboration_score}")
|
||||||
|
|
||||||
|
# Step 5: Run collaboration matching for Company A
|
||||||
|
print("\n[5] Running collaboration matching for Company A...")
|
||||||
|
service = ITAuditService(db)
|
||||||
|
matches = service.find_collaboration_matches(company_a.id)
|
||||||
|
print(f" Found {len(matches)} potential matches")
|
||||||
|
|
||||||
|
# Step 6: Save matches to database
|
||||||
|
print("\n[6] Saving matches to database...")
|
||||||
|
for match in matches:
|
||||||
|
saved_match = service.save_collaboration_match(match)
|
||||||
|
print(f" Saved: {match.company_a_name} <-> {match.company_b_name}")
|
||||||
|
print(f" Type: {match.match_type}, Score: {match.match_score}")
|
||||||
|
print(f" Reason: {match.match_reason}")
|
||||||
|
|
||||||
|
# Step 7: Verify matches in database
|
||||||
|
print("\n[7] Verifying matches in database...")
|
||||||
|
db_matches = db.query(ITCollaborationMatch).filter(
|
||||||
|
(ITCollaborationMatch.company_a_id == company_a.id) |
|
||||||
|
(ITCollaborationMatch.company_b_id == company_a.id)
|
||||||
|
).all()
|
||||||
|
|
||||||
|
print(f" Total matches in DB: {len(db_matches)}")
|
||||||
|
|
||||||
|
# Check for backup_replication match
|
||||||
|
backup_matches = [m for m in db_matches if m.match_type == 'backup_replication']
|
||||||
|
has_backup_match = len(backup_matches) > 0
|
||||||
|
|
||||||
|
if has_backup_match:
|
||||||
|
print("\n✅ SUCCESS: Backup replication match found!")
|
||||||
|
bm = backup_matches[0]
|
||||||
|
print(f" Company A ID: {bm.company_a_id}")
|
||||||
|
print(f" Company B ID: {bm.company_b_id}")
|
||||||
|
print(f" Match Score: {bm.match_score}")
|
||||||
|
print(f" Status: {bm.status}")
|
||||||
|
else:
|
||||||
|
print("\n❌ FAILED: No backup replication match found!")
|
||||||
|
|
||||||
|
# Print all match types found
|
||||||
|
print("\n[8] All match types found:")
|
||||||
|
for match in db_matches:
|
||||||
|
print(f" - {match.match_type}: {match.company_a_id} <-> {match.company_b_id} (score: {match.match_score})")
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("TEST SUMMARY")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"Companies tested: {company_a.name}, {company_b.name}")
|
||||||
|
print(f"Audits created: 2")
|
||||||
|
print(f"Matches found: {len(db_matches)}")
|
||||||
|
print(f"Backup replication match: {'YES ✅' if has_backup_match else 'NO ❌'}")
|
||||||
|
|
||||||
|
# Expected matches based on test data
|
||||||
|
expected_match_types = ['backup_replication', 'shared_licensing', 'teams_federation', 'shared_monitoring']
|
||||||
|
found_types = {m.match_type for m in db_matches}
|
||||||
|
missing_types = set(expected_match_types) - found_types
|
||||||
|
|
||||||
|
if missing_types:
|
||||||
|
print(f"\nMissing expected match types: {missing_types}")
|
||||||
|
|
||||||
|
return has_backup_match
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ ERROR: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
success = run_matching_test()
|
||||||
|
sys.exit(0 if success else 1)
|
||||||
394
tests/test_it_audit_collaboration.py
Normal file
394
tests/test_it_audit_collaboration.py
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
"""
|
||||||
|
Test IT Audit Collaboration Matching
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Tests for the collaboration matching functionality in IT Audit Service.
|
||||||
|
|
||||||
|
Test cases:
|
||||||
|
1. Backup replication matching (Proxmox PBS)
|
||||||
|
2. Shared licensing matching (M365)
|
||||||
|
3. Teams federation matching (Azure AD)
|
||||||
|
4. Shared monitoring matching (Zabbix)
|
||||||
|
5. Collective purchasing matching (similar size)
|
||||||
|
6. Knowledge sharing matching (similar tech stack)
|
||||||
|
|
||||||
|
Author: Norda Biznes Development Team
|
||||||
|
Created: 2026-01-09
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
# Add parent directory (worktree root) to path for imports
|
||||||
|
WORKTREE_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
sys.path.insert(0, WORKTREE_ROOT)
|
||||||
|
|
||||||
|
# Mock database module before importing it_audit_service
|
||||||
|
# This prevents database connection errors during import
|
||||||
|
sys.modules['database'] = MagicMock()
|
||||||
|
|
||||||
|
# Now we can safely import it_audit_service
|
||||||
|
from it_audit_service import ITAuditService, CollaborationMatch
|
||||||
|
|
||||||
|
|
||||||
|
class MockCompany:
|
||||||
|
"""Mock Company object for testing"""
|
||||||
|
def __init__(self, id: int, name: str, slug: str):
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.slug = slug
|
||||||
|
|
||||||
|
|
||||||
|
class MockITAudit:
|
||||||
|
"""Mock ITAudit object for testing"""
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.company_id = kwargs.get('company_id', 1)
|
||||||
|
|
||||||
|
# Collaboration flags
|
||||||
|
self.open_to_shared_licensing = kwargs.get('open_to_shared_licensing', False)
|
||||||
|
self.open_to_backup_replication = kwargs.get('open_to_backup_replication', False)
|
||||||
|
self.open_to_teams_federation = kwargs.get('open_to_teams_federation', False)
|
||||||
|
self.open_to_shared_monitoring = kwargs.get('open_to_shared_monitoring', False)
|
||||||
|
self.open_to_collective_purchasing = kwargs.get('open_to_collective_purchasing', False)
|
||||||
|
self.open_to_knowledge_sharing = kwargs.get('open_to_knowledge_sharing', False)
|
||||||
|
|
||||||
|
# Technology flags
|
||||||
|
self.has_azure_ad = kwargs.get('has_azure_ad', False)
|
||||||
|
self.azure_tenant_name = kwargs.get('azure_tenant_name', None)
|
||||||
|
self.has_m365 = kwargs.get('has_m365', False)
|
||||||
|
self.m365_plans = kwargs.get('m365_plans', [])
|
||||||
|
self.has_proxmox_pbs = kwargs.get('has_proxmox_pbs', False)
|
||||||
|
self.monitoring_solution = kwargs.get('monitoring_solution', None)
|
||||||
|
|
||||||
|
# Size info
|
||||||
|
self.employee_count = kwargs.get('employee_count', None)
|
||||||
|
|
||||||
|
# Tech stack
|
||||||
|
self.virtualization_platform = kwargs.get('virtualization_platform', None)
|
||||||
|
self.backup_solution = kwargs.get('backup_solution', None)
|
||||||
|
self.network_firewall_brand = kwargs.get('network_firewall_brand', None)
|
||||||
|
self.erp_system = kwargs.get('erp_system', None)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCollaborationMatching(unittest.TestCase):
|
||||||
|
"""Test collaboration matching logic without database"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Set up test fixtures"""
|
||||||
|
# Create service without database (we'll test internal methods)
|
||||||
|
self.service = ITAuditService.__new__(ITAuditService)
|
||||||
|
|
||||||
|
def test_backup_replication_match_both_have_pbs_and_open(self):
|
||||||
|
"""Test backup replication match when both companies have PBS and are open"""
|
||||||
|
company_a = MockCompany(1, "Firma A", "firma-a")
|
||||||
|
company_b = MockCompany(2, "Firma B", "firma-b")
|
||||||
|
|
||||||
|
audit_a = MockITAudit(
|
||||||
|
company_id=1,
|
||||||
|
has_proxmox_pbs=True,
|
||||||
|
open_to_backup_replication=True
|
||||||
|
)
|
||||||
|
audit_b = MockITAudit(
|
||||||
|
company_id=2,
|
||||||
|
has_proxmox_pbs=True,
|
||||||
|
open_to_backup_replication=True
|
||||||
|
)
|
||||||
|
|
||||||
|
matches = self.service._check_all_match_types(
|
||||||
|
company_a, audit_a, company_b, audit_b
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should have exactly 1 match for backup_replication
|
||||||
|
backup_matches = [m for m in matches if m.match_type == 'backup_replication']
|
||||||
|
self.assertEqual(len(backup_matches), 1)
|
||||||
|
|
||||||
|
match = backup_matches[0]
|
||||||
|
self.assertEqual(match.company_a_id, 1)
|
||||||
|
self.assertEqual(match.company_b_id, 2)
|
||||||
|
self.assertEqual(match.match_type, 'backup_replication')
|
||||||
|
self.assertEqual(match.match_score, 90)
|
||||||
|
self.assertIn('Proxmox PBS', match.match_reason)
|
||||||
|
|
||||||
|
def test_backup_replication_no_match_only_one_has_pbs(self):
|
||||||
|
"""Test no backup match when only one company has PBS"""
|
||||||
|
company_a = MockCompany(1, "Firma A", "firma-a")
|
||||||
|
company_b = MockCompany(2, "Firma B", "firma-b")
|
||||||
|
|
||||||
|
audit_a = MockITAudit(
|
||||||
|
company_id=1,
|
||||||
|
has_proxmox_pbs=True,
|
||||||
|
open_to_backup_replication=True
|
||||||
|
)
|
||||||
|
audit_b = MockITAudit(
|
||||||
|
company_id=2,
|
||||||
|
has_proxmox_pbs=False, # Company B doesn't have PBS
|
||||||
|
open_to_backup_replication=True
|
||||||
|
)
|
||||||
|
|
||||||
|
matches = self.service._check_all_match_types(
|
||||||
|
company_a, audit_a, company_b, audit_b
|
||||||
|
)
|
||||||
|
|
||||||
|
backup_matches = [m for m in matches if m.match_type == 'backup_replication']
|
||||||
|
self.assertEqual(len(backup_matches), 0)
|
||||||
|
|
||||||
|
def test_backup_replication_no_match_not_open(self):
|
||||||
|
"""Test no backup match when one company is not open"""
|
||||||
|
company_a = MockCompany(1, "Firma A", "firma-a")
|
||||||
|
company_b = MockCompany(2, "Firma B", "firma-b")
|
||||||
|
|
||||||
|
audit_a = MockITAudit(
|
||||||
|
company_id=1,
|
||||||
|
has_proxmox_pbs=True,
|
||||||
|
open_to_backup_replication=True
|
||||||
|
)
|
||||||
|
audit_b = MockITAudit(
|
||||||
|
company_id=2,
|
||||||
|
has_proxmox_pbs=True,
|
||||||
|
open_to_backup_replication=False # Company B not open
|
||||||
|
)
|
||||||
|
|
||||||
|
matches = self.service._check_all_match_types(
|
||||||
|
company_a, audit_a, company_b, audit_b
|
||||||
|
)
|
||||||
|
|
||||||
|
backup_matches = [m for m in matches if m.match_type == 'backup_replication']
|
||||||
|
self.assertEqual(len(backup_matches), 0)
|
||||||
|
|
||||||
|
def test_shared_licensing_match_both_have_m365_and_open(self):
|
||||||
|
"""Test shared licensing match when both have M365 and are open"""
|
||||||
|
company_a = MockCompany(1, "Firma A", "firma-a")
|
||||||
|
company_b = MockCompany(2, "Firma B", "firma-b")
|
||||||
|
|
||||||
|
audit_a = MockITAudit(
|
||||||
|
company_id=1,
|
||||||
|
has_m365=True,
|
||||||
|
m365_plans=['Business Basic', 'E3'],
|
||||||
|
open_to_shared_licensing=True
|
||||||
|
)
|
||||||
|
audit_b = MockITAudit(
|
||||||
|
company_id=2,
|
||||||
|
has_m365=True,
|
||||||
|
m365_plans=['E3', 'E5'],
|
||||||
|
open_to_shared_licensing=True
|
||||||
|
)
|
||||||
|
|
||||||
|
matches = self.service._check_all_match_types(
|
||||||
|
company_a, audit_a, company_b, audit_b
|
||||||
|
)
|
||||||
|
|
||||||
|
licensing_matches = [m for m in matches if m.match_type == 'shared_licensing']
|
||||||
|
self.assertEqual(len(licensing_matches), 1)
|
||||||
|
|
||||||
|
match = licensing_matches[0]
|
||||||
|
self.assertEqual(match.match_score, 80)
|
||||||
|
self.assertIn('Microsoft 365', match.match_reason)
|
||||||
|
|
||||||
|
def test_teams_federation_match_both_have_azure_ad_and_open(self):
|
||||||
|
"""Test Teams federation match when both have Azure AD and are open"""
|
||||||
|
company_a = MockCompany(1, "Firma A", "firma-a")
|
||||||
|
company_b = MockCompany(2, "Firma B", "firma-b")
|
||||||
|
|
||||||
|
audit_a = MockITAudit(
|
||||||
|
company_id=1,
|
||||||
|
has_azure_ad=True,
|
||||||
|
azure_tenant_name='firmaa.onmicrosoft.com',
|
||||||
|
open_to_teams_federation=True
|
||||||
|
)
|
||||||
|
audit_b = MockITAudit(
|
||||||
|
company_id=2,
|
||||||
|
has_azure_ad=True,
|
||||||
|
azure_tenant_name='firmab.onmicrosoft.com',
|
||||||
|
open_to_teams_federation=True
|
||||||
|
)
|
||||||
|
|
||||||
|
matches = self.service._check_all_match_types(
|
||||||
|
company_a, audit_a, company_b, audit_b
|
||||||
|
)
|
||||||
|
|
||||||
|
teams_matches = [m for m in matches if m.match_type == 'teams_federation']
|
||||||
|
self.assertEqual(len(teams_matches), 1)
|
||||||
|
|
||||||
|
match = teams_matches[0]
|
||||||
|
self.assertEqual(match.match_score, 85)
|
||||||
|
self.assertIn('Azure AD', match.match_reason)
|
||||||
|
|
||||||
|
def test_shared_monitoring_match_both_use_zabbix(self):
|
||||||
|
"""Test shared monitoring match when both use Zabbix"""
|
||||||
|
company_a = MockCompany(1, "Firma A", "firma-a")
|
||||||
|
company_b = MockCompany(2, "Firma B", "firma-b")
|
||||||
|
|
||||||
|
audit_a = MockITAudit(
|
||||||
|
company_id=1,
|
||||||
|
monitoring_solution='Zabbix',
|
||||||
|
open_to_shared_monitoring=True
|
||||||
|
)
|
||||||
|
audit_b = MockITAudit(
|
||||||
|
company_id=2,
|
||||||
|
monitoring_solution='Zabbix 6.0',
|
||||||
|
open_to_shared_monitoring=True
|
||||||
|
)
|
||||||
|
|
||||||
|
matches = self.service._check_all_match_types(
|
||||||
|
company_a, audit_a, company_b, audit_b
|
||||||
|
)
|
||||||
|
|
||||||
|
monitoring_matches = [m for m in matches if m.match_type == 'shared_monitoring']
|
||||||
|
self.assertEqual(len(monitoring_matches), 1)
|
||||||
|
|
||||||
|
match = monitoring_matches[0]
|
||||||
|
self.assertEqual(match.match_score, 75)
|
||||||
|
self.assertIn('Zabbix', match.match_reason)
|
||||||
|
|
||||||
|
def test_collective_purchasing_match_similar_size(self):
|
||||||
|
"""Test collective purchasing match for similar company sizes"""
|
||||||
|
company_a = MockCompany(1, "Firma A", "firma-a")
|
||||||
|
company_b = MockCompany(2, "Firma B", "firma-b")
|
||||||
|
|
||||||
|
audit_a = MockITAudit(
|
||||||
|
company_id=1,
|
||||||
|
employee_count='11-50',
|
||||||
|
open_to_collective_purchasing=True
|
||||||
|
)
|
||||||
|
audit_b = MockITAudit(
|
||||||
|
company_id=2,
|
||||||
|
employee_count='51-100', # Similar size (within 3x ratio)
|
||||||
|
open_to_collective_purchasing=True
|
||||||
|
)
|
||||||
|
|
||||||
|
matches = self.service._check_all_match_types(
|
||||||
|
company_a, audit_a, company_b, audit_b
|
||||||
|
)
|
||||||
|
|
||||||
|
purchasing_matches = [m for m in matches if m.match_type == 'collective_purchasing']
|
||||||
|
self.assertEqual(len(purchasing_matches), 1)
|
||||||
|
|
||||||
|
match = purchasing_matches[0]
|
||||||
|
self.assertEqual(match.match_score, 70)
|
||||||
|
|
||||||
|
def test_knowledge_sharing_match_similar_tech_stack(self):
|
||||||
|
"""Test knowledge sharing match for similar tech stack"""
|
||||||
|
company_a = MockCompany(1, "Firma A", "firma-a")
|
||||||
|
company_b = MockCompany(2, "Firma B", "firma-b")
|
||||||
|
|
||||||
|
audit_a = MockITAudit(
|
||||||
|
company_id=1,
|
||||||
|
virtualization_platform='Proxmox',
|
||||||
|
backup_solution='Veeam',
|
||||||
|
has_azure_ad=True,
|
||||||
|
has_m365=True,
|
||||||
|
open_to_knowledge_sharing=True
|
||||||
|
)
|
||||||
|
audit_b = MockITAudit(
|
||||||
|
company_id=2,
|
||||||
|
virtualization_platform='Proxmox',
|
||||||
|
backup_solution='Veeam',
|
||||||
|
has_azure_ad=True,
|
||||||
|
has_m365=True,
|
||||||
|
open_to_knowledge_sharing=True
|
||||||
|
)
|
||||||
|
|
||||||
|
matches = self.service._check_all_match_types(
|
||||||
|
company_a, audit_a, company_b, audit_b
|
||||||
|
)
|
||||||
|
|
||||||
|
knowledge_matches = [m for m in matches if m.match_type == 'knowledge_sharing']
|
||||||
|
self.assertEqual(len(knowledge_matches), 1)
|
||||||
|
|
||||||
|
match = knowledge_matches[0]
|
||||||
|
self.assertEqual(match.match_score, 65)
|
||||||
|
# Should have at least 2 common technologies
|
||||||
|
self.assertIn('common_tech', match.shared_attributes)
|
||||||
|
self.assertGreaterEqual(len(match.shared_attributes['common_tech']), 2)
|
||||||
|
|
||||||
|
def test_multiple_matches_for_same_companies(self):
|
||||||
|
"""Test that multiple match types can be found for the same company pair"""
|
||||||
|
company_a = MockCompany(1, "Firma A", "firma-a")
|
||||||
|
company_b = MockCompany(2, "Firma B", "firma-b")
|
||||||
|
|
||||||
|
# Both companies have multiple collaboration opportunities
|
||||||
|
audit_a = MockITAudit(
|
||||||
|
company_id=1,
|
||||||
|
has_proxmox_pbs=True,
|
||||||
|
has_azure_ad=True,
|
||||||
|
has_m365=True,
|
||||||
|
open_to_backup_replication=True,
|
||||||
|
open_to_teams_federation=True,
|
||||||
|
open_to_shared_licensing=True
|
||||||
|
)
|
||||||
|
audit_b = MockITAudit(
|
||||||
|
company_id=2,
|
||||||
|
has_proxmox_pbs=True,
|
||||||
|
has_azure_ad=True,
|
||||||
|
has_m365=True,
|
||||||
|
open_to_backup_replication=True,
|
||||||
|
open_to_teams_federation=True,
|
||||||
|
open_to_shared_licensing=True
|
||||||
|
)
|
||||||
|
|
||||||
|
matches = self.service._check_all_match_types(
|
||||||
|
company_a, audit_a, company_b, audit_b
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should have matches for backup_replication, teams_federation, and shared_licensing
|
||||||
|
match_types = {m.match_type for m in matches}
|
||||||
|
self.assertIn('backup_replication', match_types)
|
||||||
|
self.assertIn('teams_federation', match_types)
|
||||||
|
self.assertIn('shared_licensing', match_types)
|
||||||
|
self.assertEqual(len(matches), 3)
|
||||||
|
|
||||||
|
def test_parse_count_range(self):
|
||||||
|
"""Test employee count range parsing"""
|
||||||
|
self.assertEqual(self.service._parse_count_range('1-10'), 5)
|
||||||
|
self.assertEqual(self.service._parse_count_range('11-50'), 30)
|
||||||
|
self.assertEqual(self.service._parse_count_range('51-100'), 75)
|
||||||
|
self.assertEqual(self.service._parse_count_range('101-250'), 175)
|
||||||
|
self.assertEqual(self.service._parse_count_range('251-500'), 375)
|
||||||
|
self.assertEqual(self.service._parse_count_range('500+'), 750)
|
||||||
|
self.assertIsNone(self.service._parse_count_range(None))
|
||||||
|
self.assertIsNone(self.service._parse_count_range('unknown'))
|
||||||
|
|
||||||
|
def test_sizes_similar(self):
|
||||||
|
"""Test company size similarity check"""
|
||||||
|
# Within 3x ratio should be similar
|
||||||
|
self.assertTrue(self.service._sizes_similar(30, 75)) # 2.5x
|
||||||
|
self.assertTrue(self.service._sizes_similar(30, 30)) # 1x
|
||||||
|
self.assertTrue(self.service._sizes_similar(30, 50)) # 1.67x
|
||||||
|
|
||||||
|
# More than 3x ratio should not be similar
|
||||||
|
self.assertFalse(self.service._sizes_similar(5, 175)) # 35x
|
||||||
|
self.assertFalse(self.service._sizes_similar(5, 750)) # 150x
|
||||||
|
|
||||||
|
# Zero handling
|
||||||
|
self.assertFalse(self.service._sizes_similar(0, 50))
|
||||||
|
self.assertFalse(self.service._sizes_similar(50, 0))
|
||||||
|
|
||||||
|
|
||||||
|
class TestCollaborationMatchDataclass(unittest.TestCase):
|
||||||
|
"""Test CollaborationMatch dataclass"""
|
||||||
|
|
||||||
|
def test_collaboration_match_creation(self):
|
||||||
|
"""Test creating a CollaborationMatch"""
|
||||||
|
match = CollaborationMatch(
|
||||||
|
company_a_id=1,
|
||||||
|
company_b_id=2,
|
||||||
|
company_a_name="Firma A",
|
||||||
|
company_b_name="Firma B",
|
||||||
|
match_type="backup_replication",
|
||||||
|
match_reason="Obie firmy używają Proxmox PBS",
|
||||||
|
match_score=90,
|
||||||
|
shared_attributes={"pbs": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(match.company_a_id, 1)
|
||||||
|
self.assertEqual(match.company_b_id, 2)
|
||||||
|
self.assertEqual(match.match_type, "backup_replication")
|
||||||
|
self.assertEqual(match.match_score, 90)
|
||||||
|
self.assertEqual(match.shared_attributes["pbs"], True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2)
|
||||||
Loading…
Reference in New Issue
Block a user