diff --git a/src/MedFiEscrow.sol b/src/MedFiEscrow.sol index 2f853e8..3e4f088 100644 --- a/src/MedFiEscrow.sol +++ b/src/MedFiEscrow.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; + import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; diff --git a/src/MedFiRecords.sol b/src/MedFiRecords.sol index 8b9feb4..bda33c4 100644 --- a/src/MedFiRecords.sol +++ b/src/MedFiRecords.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; + import {IMedFiRegistry, IMedFiEscrow} from "./interface/Interfaces.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; diff --git a/src/MedFiRegistry.sol b/src/MedFiRegistry.sol index 4c4a521..9369272 100644 --- a/src/MedFiRegistry.sol +++ b/src/MedFiRegistry.sol @@ -6,23 +6,22 @@ import "@openzeppelin/contracts/access/Ownable.sol"; /** * @title MedFiRegistry * @author Therock Ani - * @notice A scalable registry contract for managing hospitals and medical professionals with role-based administration, - * address-based verification, and blacklisting functionality. + * @notice A scalable registry contract for managing hospitals and medical professionals with admin-only verification + * and blacklisting functionality. * @dev This contract uses OpenZeppelin's Ownable for ownership control and includes custom errors for gas-efficient error handling. * All metadata (names, locations, etc.) are stored off-chain and managed by frontend/backend. */ contract MedFiRegistry is Ownable { // Custom Errors error OnlyAdminAllowed(); - error HospitalNotVerified(); error HospitalAlreadyRegistered(); error HospitalNotRegistered(); + error HospitalNotVerified(); + error HospitalIsBlacklisted(); error ProfessionalAlreadyRegistered(); - error ProfessionalIsBlacklisted(); error ProfessionalNotRegistered(); - error ProfessionalNotAffiliated(); - error InvalidHospitalAddress(); - error UnauthorizedAffiliation(); + error ProfessionalIsBlacklisted(); + error InvalidAddress(); /** * @notice Mapping of addresses to their admin status. @@ -35,20 +34,15 @@ contract MedFiRegistry is Ownable { mapping(address => Hospital) public hospitals; /** - * @notice Unified mapping of professional addresses to their Professional struct. + * @notice Mapping of professional addresses to their Professional struct. */ mapping(address => Professional) public professionals; /** - * @notice Mapping of addresses to their blacklist status. + * @notice Mapping of addresses to their blacklist status (for both hospitals and professionals). */ mapping(address => bool) public isBlacklisted; - /** - * @notice Mapping to track pending affiliations (hospital => professional => isPending). - */ - mapping(address => mapping(address => bool)) public pendingAffiliations; - /** * @notice Array to store all registered professional addresses for enumeration. */ @@ -70,12 +64,12 @@ contract MedFiRegistry is Ownable { mapping(address => bool) private isHospitalInArray; /** - * @notice Struct to store hospital details and affiliated professionals. + * @notice Struct to store hospital details. */ struct Hospital { bool isVerified; uint256 registrationTimestamp; - mapping(address => bool) affiliatedProfessionals; + bool isActive; } /** @@ -83,9 +77,7 @@ contract MedFiRegistry is Ownable { */ struct Professional { address professionalAddress; - address affiliatedHospital; bool isVerified; - bool isAffiliated; uint256 registrationTimestamp; bool isActive; } @@ -94,11 +86,9 @@ contract MedFiRegistry is Ownable { event HospitalRegistered(address indexed hospital); event HospitalVerified(address indexed hospital); event HospitalUnverified(address indexed hospital); - event AffiliationApproved(address indexed hospital, address indexed professional); - event AffiliationRevoked(address indexed hospital, address indexed professional); - event ProfessionalRegistered(address indexed professional, address indexed hospital); - event AffiliationChanged(address indexed professional, address indexed oldHospital, address indexed newHospital); - event ProfessionalUnaffiliated(address indexed professional, address indexed hospital); + event HospitalBlacklisted(address indexed hospital); + event HospitalUnblacklisted(address indexed hospital); + event ProfessionalRegistered(address indexed professional); event ProfessionalVerified(address indexed professional); event ProfessionalUnverified(address indexed professional); event ProfessionalProfileUpdated(address indexed professional); @@ -114,19 +104,12 @@ contract MedFiRegistry is Ownable { } /** - * @notice Restricts function access to verified hospitals only. - */ - modifier onlyVerifiedHospital(address _hospital) { - if (!hospitals[_hospital].isVerified) revert HospitalNotVerified(); - _; - } - - /** - * @notice Restricts function access to affiliated and verified professionals only. + * @notice Restricts function access to registered and active professionals only. */ - modifier onlyAffiliatedProfessional() { + modifier onlyActiveProfessional() { Professional memory professional = professionals[msg.sender]; - if (!professional.isAffiliated || !professional.isActive) revert ProfessionalNotAffiliated(); + if (professional.professionalAddress == address(0)) revert ProfessionalNotRegistered(); + if (!professional.isActive) revert ProfessionalIsBlacklisted(); _; } @@ -141,6 +124,7 @@ contract MedFiRegistry is Ownable { * @notice Adds an address to the admin list. */ function addAdmin(address _admin) external onlyOwner { + if (_admin == address(0)) revert InvalidAddress(); isAdmin[_admin] = true; } @@ -157,9 +141,9 @@ contract MedFiRegistry is Ownable { */ function registerHospital() external { if (hospitals[msg.sender].registrationTimestamp != 0) revert HospitalAlreadyRegistered(); + if (isBlacklisted[msg.sender]) revert HospitalIsBlacklisted(); - hospitals[msg.sender].registrationTimestamp = block.timestamp; - hospitals[msg.sender].isVerified = false; + hospitals[msg.sender] = Hospital({registrationTimestamp: block.timestamp, isVerified: false, isActive: true}); if (!isHospitalInArray[msg.sender]) { hospitalAddresses.push(msg.sender); @@ -174,6 +158,7 @@ contract MedFiRegistry is Ownable { */ function verifyHospital(address _hospital) external onlyAdmin { if (hospitals[_hospital].registrationTimestamp == 0) revert HospitalNotRegistered(); + if (isBlacklisted[_hospital]) revert HospitalIsBlacklisted(); hospitals[_hospital].isVerified = true; emit HospitalVerified(_hospital); } @@ -188,60 +173,37 @@ contract MedFiRegistry is Ownable { } /** - * @notice Allows a verified hospital to approve a professional for affiliation. + * @notice Blacklists a hospital, preventing further activities and revoking verification. */ - function approveAffiliation(address _professionalAddress) external onlyVerifiedHospital(msg.sender) { - if (_professionalAddress == address(0)) revert InvalidHospitalAddress(); + function blacklistHospital(address _hospital) external onlyAdmin { + isBlacklisted[_hospital] = true; - pendingAffiliations[msg.sender][_professionalAddress] = true; - emit AffiliationApproved(msg.sender, _professionalAddress); - } - - /** - * @notice Allows a verified hospital to approve multiple professionals for affiliation. - */ - function approveMultipleAffiliations(address[] memory _professionalAddresses) - external - onlyVerifiedHospital(msg.sender) - { - for (uint256 i = 0; i < _professionalAddresses.length; i++) { - address professionalAddress = _professionalAddresses[i]; - if (professionalAddress != address(0)) { - pendingAffiliations[msg.sender][professionalAddress] = true; - emit AffiliationApproved(msg.sender, professionalAddress); - } + if (hospitals[_hospital].registrationTimestamp != 0) { + hospitals[_hospital].isVerified = false; + hospitals[_hospital].isActive = false; } - } - /** - * @notice Allows a hospital to revoke affiliation approval for a professional. - */ - function revokeAffiliationApproval(address _professionalAddress) external onlyVerifiedHospital(msg.sender) { - pendingAffiliations[msg.sender][_professionalAddress] = false; - emit AffiliationRevoked(msg.sender, _professionalAddress); + emit HospitalBlacklisted(_hospital); } /** - * @notice Allows a hospital to revoke affiliation approval for multiple professionals. + * @notice Removes a hospital from the blacklist. */ - function revokeMultipleAffiliationApprovals(address[] memory _professionalAddresses) - external - onlyVerifiedHospital(msg.sender) - { - for (uint256 i = 0; i < _professionalAddresses.length; i++) { - address professionalAddress = _professionalAddresses[i]; - if (professionalAddress != address(0)) { - pendingAffiliations[msg.sender][professionalAddress] = false; - emit AffiliationRevoked(msg.sender, professionalAddress); - } + function unblacklistHospital(address _hospital) external onlyAdmin { + isBlacklisted[_hospital] = false; + + if (hospitals[_hospital].registrationTimestamp != 0) { + hospitals[_hospital].isActive = true; } + + emit HospitalUnblacklisted(_hospital); } /** - * @notice Allows a professional to register with a hospital that has approved their affiliation. + * @notice Allows a professional to register themselves. * @dev Metadata stored off-chain. Emits `ProfessionalRegistered`. */ - function registerProfessional(address _hospitalAddress) external { + function registerProfessional() external { if (professionals[msg.sender].professionalAddress != address(0)) { revert ProfessionalAlreadyRegistered(); } @@ -250,134 +212,55 @@ contract MedFiRegistry is Ownable { revert ProfessionalIsBlacklisted(); } - if (_hospitalAddress == address(0)) revert InvalidHospitalAddress(); - if (hospitals[_hospitalAddress].registrationTimestamp == 0) revert HospitalNotRegistered(); - if (!hospitals[_hospitalAddress].isVerified) revert HospitalNotVerified(); - - if (!pendingAffiliations[_hospitalAddress][msg.sender]) { - revert UnauthorizedAffiliation(); - } - professionals[msg.sender] = Professional({ - professionalAddress: msg.sender, - affiliatedHospital: _hospitalAddress, - isVerified: false, - isAffiliated: true, - registrationTimestamp: block.timestamp, - isActive: true + professionalAddress: msg.sender, isVerified: false, registrationTimestamp: block.timestamp, isActive: true }); - hospitals[_hospitalAddress].affiliatedProfessionals[msg.sender] = true; - pendingAffiliations[_hospitalAddress][msg.sender] = false; - if (!isProfessionalInArray[msg.sender]) { professionalAddresses.push(msg.sender); isProfessionalInArray[msg.sender] = true; } - emit ProfessionalRegistered(msg.sender, _hospitalAddress); + emit ProfessionalRegistered(msg.sender); } /** * @notice Allows a professional to update their profile information. * @dev Metadata updated off-chain. Emits `ProfessionalProfileUpdated`. */ - function updateProfessionalProfile() external onlyAffiliatedProfessional { + function updateProfessionalProfile() external onlyActiveProfessional { emit ProfessionalProfileUpdated(msg.sender); } /** - * @notice Allows a hospital to unaffiliate a professional, rendering them inactive. - */ - function unaffiliateProfessional(address _professionalAddress) external { - Professional storage professional = professionals[_professionalAddress]; - - if (professional.affiliatedHospital != msg.sender) revert UnauthorizedAffiliation(); - if (!professional.isAffiliated) revert ProfessionalNotAffiliated(); - - professional.isAffiliated = false; - professional.isVerified = false; - professional.isActive = false; - - hospitals[msg.sender].affiliatedProfessionals[_professionalAddress] = false; - - emit ProfessionalUnaffiliated(_professionalAddress, msg.sender); - } - - /** - * @notice Allows a hospital to unaffiliate multiple professionals, rendering them inactive. - */ - function unaffiliateMultipleProfessionals(address[] memory _professionalAddresses) external { - for (uint256 i = 0; i < _professionalAddresses.length; i++) { - address professionalAddress = _professionalAddresses[i]; - if (professionalAddress != address(0)) { - Professional storage professional = professionals[professionalAddress]; - - if (professional.affiliatedHospital == msg.sender && professional.isAffiliated) { - professional.isAffiliated = false; - professional.isVerified = false; - professional.isActive = false; - - hospitals[msg.sender].affiliatedProfessionals[professionalAddress] = false; - - emit ProfessionalUnaffiliated(professionalAddress, msg.sender); - } - } - } - } - - /** - * @notice Allows a professional to change their hospital affiliation. + * @notice Verifies a registered professional. */ - function changeAffiliation(address _newHospitalAddress) external { - Professional storage professional = professionals[msg.sender]; - + function verifyProfessional(address _professional) external onlyAdmin { + Professional storage professional = professionals[_professional]; if (professional.professionalAddress == address(0)) { revert ProfessionalNotRegistered(); } - - if (isBlacklisted[msg.sender]) { + if (isBlacklisted[_professional]) { revert ProfessionalIsBlacklisted(); } - - if (_newHospitalAddress == address(0)) revert InvalidHospitalAddress(); - if (hospitals[_newHospitalAddress].registrationTimestamp == 0) revert HospitalNotRegistered(); - if (!hospitals[_newHospitalAddress].isVerified) revert HospitalNotVerified(); - - if (!pendingAffiliations[_newHospitalAddress][msg.sender]) { - revert UnauthorizedAffiliation(); - } - - address oldHospital = professional.affiliatedHospital; - if (oldHospital != address(0) && professional.isAffiliated) { - hospitals[oldHospital].affiliatedProfessionals[msg.sender] = false; - emit ProfessionalUnaffiliated(msg.sender, oldHospital); - } - - professional.affiliatedHospital = _newHospitalAddress; - professional.isAffiliated = true; - professional.isVerified = false; - professional.isActive = true; - - hospitals[_newHospitalAddress].affiliatedProfessionals[msg.sender] = true; - pendingAffiliations[_newHospitalAddress][msg.sender] = false; - - emit AffiliationChanged(msg.sender, oldHospital, _newHospitalAddress); + professional.isVerified = true; + emit ProfessionalVerified(_professional); } /** - * @notice Verifies a registered professional. + * @notice Verifies multiple professionals at once. */ - function verifyProfessional(address _professional) external onlyAdmin { - Professional storage professional = professionals[_professional]; - if (professional.professionalAddress == address(0)) { - revert ProfessionalNotRegistered(); - } - if (!professional.isAffiliated) { - revert ProfessionalNotAffiliated(); + function verifyMultipleProfessionals(address[] memory _professionals) external onlyAdmin { + for (uint256 i = 0; i < _professionals.length; i++) { + address professionalAddress = _professionals[i]; + if (professionalAddress != address(0)) { + Professional storage professional = professionals[professionalAddress]; + if (professional.professionalAddress != address(0) && !isBlacklisted[professionalAddress]) { + professional.isVerified = true; + emit ProfessionalVerified(professionalAddress); + } + } } - professional.isVerified = true; - emit ProfessionalVerified(_professional); } /** @@ -391,6 +274,21 @@ contract MedFiRegistry is Ownable { emit ProfessionalUnverified(_professional); } + /** + * @notice Revokes verification from multiple professionals at once. + */ + function unverifyMultipleProfessionals(address[] memory _professionals) external onlyAdmin { + for (uint256 i = 0; i < _professionals.length; i++) { + address professionalAddress = _professionals[i]; + if (professionalAddress != address(0)) { + if (professionals[professionalAddress].professionalAddress != address(0)) { + professionals[professionalAddress].isVerified = false; + emit ProfessionalUnverified(professionalAddress); + } + } + } + } + /** * @notice Blacklists a professional, preventing further activities and revoking verification. */ @@ -411,8 +309,7 @@ contract MedFiRegistry is Ownable { function unblacklistProfessional(address _professional) external onlyAdmin { isBlacklisted[_professional] = false; - if (professionals[_professional].professionalAddress != address(0) && professionals[_professional].isAffiliated) - { + if (professionals[_professional].professionalAddress != address(0)) { professionals[_professional].isActive = true; } @@ -420,28 +317,42 @@ contract MedFiRegistry is Ownable { } /** - * @notice Checks if a professional is verified and active. + * @notice Checks if a hospital is verified and active. */ - function isVerified(address _professional) external view returns (bool) { - if (isBlacklisted[_professional]) return false; - Professional memory professional = professionals[_professional]; - return professional.isVerified && professional.isAffiliated && professional.isActive; + function isVerifiedHospital(address _hospital) external view returns (bool) { + if (isBlacklisted[_hospital]) return false; + Hospital memory hospital = hospitals[_hospital]; + return hospital.isVerified && hospital.isActive; } /** - * @notice Checks if a professional can perform activities. + * @notice Checks if a professional is verified and active. */ - function canPerformActivities(address _professional) external view returns (bool) { + function isVerifiedProfessional(address _professional) external view returns (bool) { if (isBlacklisted[_professional]) return false; Professional memory professional = professionals[_professional]; - return professional.isVerified && professional.isAffiliated && professional.isActive; + return professional.isVerified && professional.isActive; } /** - * @notice Checks if a hospital has approved affiliation for a professional. + * @notice Checks if an address (hospital or professional) can perform activities. */ - function isAffiliationApproved(address _hospital, address _professional) external view returns (bool) { - return pendingAffiliations[_hospital][_professional]; + function canPerformActivities(address _address) external view returns (bool) { + if (isBlacklisted[_address]) return false; + + // Check if it's a hospital + if (hospitals[_address].registrationTimestamp != 0) { + Hospital memory hospital = hospitals[_address]; + return hospital.isVerified && hospital.isActive; + } + + // Check if it's a professional + if (professionals[_address].professionalAddress != address(0)) { + Professional memory professional = professionals[_address]; + return professional.isVerified && professional.isActive; + } + + return false; } /** @@ -454,9 +365,13 @@ contract MedFiRegistry is Ownable { /** * @notice Gets hospital details by address. */ - function getHospital(address _hospital) external view returns (bool verified, uint256 registrationTimestamp) { - Hospital storage hospital = hospitals[_hospital]; - return (hospital.isVerified, hospital.registrationTimestamp); + function getHospital(address _hospital) + external + view + returns (bool verified, uint256 registrationTimestamp, bool active) + { + Hospital memory hospital = hospitals[_hospital]; + return (hospital.isVerified, hospital.registrationTimestamp, hospital.isActive); } /** @@ -486,11 +401,4 @@ contract MedFiRegistry is Ownable { function getTotalHospitals() external view returns (uint256) { return hospitalAddresses.length; } - - /** - * @notice Checks if a professional is affiliated with a specific hospital. - */ - function isProfessionalAffiliated(address _hospital, address _professional) external view returns (bool) { - return hospitals[_hospital].affiliatedProfessionals[_professional]; - } } diff --git a/test/MedFiEdgeCaseTest.t.sol b/test/MedFiEdgeCaseTest.t.sol index e05e905..23fdb97 100644 --- a/test/MedFiEdgeCaseTest.t.sol +++ b/test/MedFiEdgeCaseTest.t.sol @@ -7,7 +7,7 @@ import "../src/MedFiEscrow.sol"; import "../src/MedFiRecords.sol"; import "./mock/MockUSDC.sol"; -contract MedFiEdgeCaseTest is Test { +contract MedFiEdgeCaseTst is Test { MedFiRegistry public registry; MedFiEscrow public escrow; MedFiRecords public records; @@ -19,7 +19,7 @@ contract MedFiEdgeCaseTest is Test { address public doctor = makeAddr("doctor"); address public patient = makeAddr("patient"); - uint256 constant STORAGE_FEE = 1e6; // 1 USDC + uint256 constant STORAGE_FEE = 1e6; function setUp() public { vm.startPrank(owner); @@ -58,67 +58,75 @@ contract MedFiEdgeCaseTest is Test { registry.unverifyHospital(hospital); } - function testProfessionalRegistrationWithoutApproval() public { - _setupVerifiedHospital(); + function testProfessionalDoubleRegistration() public { + vm.prank(doctor); + registry.registerProfessional(); vm.prank(doctor); - vm.expectRevert(MedFiRegistry.UnauthorizedAffiliation.selector); - registry.registerProfessional(hospital); + vm.expectRevert(MedFiRegistry.ProfessionalAlreadyRegistered.selector); + registry.registerProfessional(); } - function testProfessionalRegistrationWithUnverifiedHospital() public { - vm.prank(hospital); - registry.registerHospital(); - - vm.prank(hospital); - vm.expectRevert(MedFiRegistry.HospitalNotVerified.selector); - registry.approveAffiliation(doctor); + function testBlacklistedProfessionalCannotRegister() public { + vm.prank(admin); + registry.blacklistProfessional(doctor); vm.prank(doctor); - vm.expectRevert(MedFiRegistry.HospitalNotVerified.selector); - registry.registerProfessional(hospital); + vm.expectRevert(MedFiRegistry.ProfessionalIsBlacklisted.selector); + registry.registerProfessional(); } - function testChangeAffiliationToSameHospital() public { - _setupVerifiedDoctor(); + function testBlacklistedHospitalCannotRegister() public { + vm.prank(admin); + registry.blacklistHospital(hospital); vm.prank(hospital); - registry.approveAffiliation(doctor); + vm.expectRevert(MedFiRegistry.HospitalIsBlacklisted.selector); + registry.registerHospital(); + } + function testUnblacklistRestoresActiveStatus() public { vm.prank(doctor); - registry.changeAffiliation(hospital); + registry.registerProfessional(); - MedFiRegistry.Professional memory prof = registry.getProfessional(doctor); - assertEq(prof.affiliatedHospital, hospital); - assertFalse(prof.isVerified); - } + vm.prank(admin); + registry.verifyProfessional(doctor); - function testUnaffiliateNonAffiliatedProfessional() public { - _setupVerifiedDoctor(); + vm.prank(admin); + registry.blacklistProfessional(doctor); - address otherHospital = makeAddr("otherHospital"); - vm.prank(otherHospital); - registry.registerHospital(); + assertFalse(registry.canPerformActivities(doctor)); vm.prank(admin); - registry.verifyHospital(otherHospital); + registry.unblacklistProfessional(doctor); - vm.prank(otherHospital); - vm.expectRevert(MedFiRegistry.UnauthorizedAffiliation.selector); - registry.unaffiliateProfessional(doctor); + MedFiRegistry.Professional memory prof = registry.getProfessional(doctor); + assertTrue(prof.isActive); + assertFalse(prof.isVerified); // Verification not restored } - function testBlacklistAfterUnaffiliation() public { - _setupVerifiedDoctor(); + function testCannotVerifyBlacklistedProfessional() public { + vm.prank(doctor); + registry.registerProfessional(); + + vm.prank(admin); + registry.blacklistProfessional(doctor); + vm.prank(admin); + vm.expectRevert(MedFiRegistry.ProfessionalIsBlacklisted.selector); + registry.verifyProfessional(doctor); + } + + function testCannotVerifyBlacklistedHospital() public { vm.prank(hospital); - registry.unaffiliateProfessional(doctor); + registry.registerHospital(); vm.prank(admin); - registry.blacklistProfessional(doctor); + registry.blacklistHospital(hospital); - assertTrue(registry.isBlacklisted(doctor)); - assertFalse(registry.canPerformActivities(doctor)); + vm.prank(admin); + vm.expectRevert(MedFiRegistry.HospitalIsBlacklisted.selector); + registry.verifyHospital(hospital); } // ===== ESCROW EDGE CASES ===== @@ -217,14 +225,10 @@ contract MedFiEdgeCaseTest is Test { } function testGrantAccessToUnverifiedProfessional() public { - _setupVerifiedHospital(); _setupPatientSubscription(); - vm.prank(hospital); - registry.approveAffiliation(doctor); - vm.prank(doctor); - registry.registerProfessional(hospital); + registry.registerProfessional(); vm.prank(patient); vm.expectRevert(MedFiRecords.CannotPerformActivities.selector); @@ -294,7 +298,6 @@ contract MedFiEdgeCaseTest is Test { } function testRateWithInvalidRating() public { - // _setupVerifiedDoctor(); _createConfirmedBooking(); vm.prank(patient); @@ -311,13 +314,8 @@ contract MedFiEdgeCaseTest is Test { } function testRateUnverifiedHealthWorker() public { - _setupVerifiedHospital(); - - vm.prank(hospital); - registry.approveAffiliation(doctor); - vm.prank(doctor); - registry.registerProfessional(hospital); + registry.registerProfessional(); vm.prank(patient); vm.expectRevert(MedFiRecords.CannotPerformActivities.selector); @@ -366,20 +364,14 @@ contract MedFiEdgeCaseTest is Test { } function testAccessControlWithMultipleProfessionals() public { - _setupVerifiedHospital(); _setupPatientSubscription(); address doctor2 = makeAddr("doctor2"); - vm.prank(hospital); - registry.approveAffiliation(doctor); - vm.prank(hospital); - registry.approveAffiliation(doctor2); - vm.prank(doctor); - registry.registerProfessional(hospital); + registry.registerProfessional(); vm.prank(doctor2); - registry.registerProfessional(hospital); + registry.registerProfessional(); vm.prank(admin); registry.verifyProfessional(doctor); @@ -560,22 +552,9 @@ contract MedFiEdgeCaseTest is Test { // ===== HELPER FUNCTIONS ===== - function _setupVerifiedHospital() internal { - vm.prank(hospital); - registry.registerHospital(); - - vm.prank(admin); - registry.verifyHospital(hospital); - } - function _setupVerifiedDoctor() internal { - _setupVerifiedHospital(); - - vm.prank(hospital); - registry.approveAffiliation(doctor); - vm.prank(doctor); - registry.registerProfessional(hospital); + registry.registerProfessional(); vm.prank(admin); registry.verifyProfessional(doctor); diff --git a/test/MedFiIntegrationTest.t.sol b/test/MedFiIntegrationTest.t.sol index 700ee21..1aaf1db 100644 --- a/test/MedFiIntegrationTest.t.sol +++ b/test/MedFiIntegrationTest.t.sol @@ -7,7 +7,7 @@ import "../src/MedFiEscrow.sol"; import "../src/MedFiRecords.sol"; import "./mock/MockUSDC.sol"; -contract MedFiIntegrationTest is Test { +contract MedFiIntegrationTst is Test { MedFiRegistry public registry; MedFiEscrow public escrow; MedFiRecords public records; @@ -23,10 +23,10 @@ contract MedFiIntegrationTest is Test { address public patient2 = makeAddr("patient2"); address public patient3 = makeAddr("patient3"); - uint256 public constant FEE_PERCENT = 10; // 10% for testing - uint256 public constant INITIAL_USDC_AMOUNT = 10000 * 10 ** 6; // 10,000 USDC - uint256 public constant ONE_USDC_AMOUNT = 1 * 10 ** 6; // 1 USDC - uint256 public constant STORAGE_FEE = 1 * 10 ** 6; // 1 USDC + uint256 public constant FEE_PERCENT = 10; + uint256 public constant INITIAL_USDC_AMOUNT = 10000 * 10 ** 6; + uint256 public constant ONE_USDC_AMOUNT = 1 * 10 ** 6; + uint256 public constant STORAGE_FEE = 1 * 10 ** 6; function setUp() public { vm.startPrank(owner); @@ -47,40 +47,32 @@ contract MedFiIntegrationTest is Test { // ===== INTEGRATION TESTS ===== function testCompletePatientJourney() public { - // 1. Setup hospital and doctor _setupVerifiedDoctor(); - // 2. Patient approves USDC for all operations vm.prank(patient1); - usdc.approve(address(records), STORAGE_FEE * 10); // For subscription + usdc.approve(address(records), STORAGE_FEE * 10); vm.prank(patient1); - usdc.approve(address(escrow), 1000 * 10 ** 6); // For bookings + usdc.approve(address(escrow), 1000 * 10 ** 6); - // 3. Patient grants access to doctor (triggers subscription payment) vm.prank(patient1); records.grantAccess(doctor1); - // 4. Patient books service - uint256 bookingAmount = 200 * 10 ** 6; // 200 USDC + uint256 bookingAmount = 200 * 10 ** 6; vm.prank(patient1); escrow.bookService(doctor1, bookingAmount); - // 5. Doctor adds medical record vm.prank(doctor1); records.storeRecordByCareProvider(patient1, "QmHealthRecord123"); - // 6. Patient confirms service vm.prank(patient1); escrow.confirmService(1); - // 7. Patient rates doctor vm.prank(patient1); records.rateHealthWorker(doctor1, 5); (uint256 recordCount,) = records.getProfessionalStats(doctor1); - // Verify final state assertTrue(escrow.getBooking(1).confirmed); assertEq(records.getAverageRating(doctor1), 5); assertEq(recordCount, 1); @@ -92,19 +84,16 @@ contract MedFiIntegrationTest is Test { function testMultipleBookingsAndRatings() public { _setupVerifiedDoctor(); - // Patient approves USDC for all operations vm.prank(patient1); usdc.approve(address(records), STORAGE_FEE * 10); vm.prank(patient2); usdc.approve(address(records), STORAGE_FEE * 10); - // Patients grant access vm.prank(patient1); records.grantAccess(doctor1); vm.prank(patient2); records.grantAccess(doctor1); - // Create multiple bookings uint256 bookingAmount = 100 * 10 ** 6; for (uint256 i = 0; i < 3; i++) { address patient = i == 0 ? patient1 : patient2; @@ -116,7 +105,6 @@ contract MedFiIntegrationTest is Test { escrow.bookService(doctor1, bookingAmount); } - // Confirm all bookings vm.prank(patient1); escrow.confirmService(1); @@ -126,67 +114,29 @@ contract MedFiIntegrationTest is Test { vm.prank(patient2); escrow.confirmService(3); - // Rate the doctor vm.prank(patient1); records.rateHealthWorker(doctor1, 4); vm.prank(patient2); records.rateHealthWorker(doctor1, 5); - // Verify stats (uint256 confirmedBookings, uint256 totalEarnings,) = escrow.getProviderStats(doctor1); assertEq(confirmedBookings, 3); (uint256 avgRating, uint256 totalRatings,) = records.getHealthWorkerRatingDetails(doctor1); assertEq(totalRatings, 2); - assertEq(avgRating, 4); // (4 + 5) / 2 = 4 (integer division) - } - - function testHospitalAffiliationChanges() public { - // Setup two hospitals - _setupTwoHospitals(); - - // Doctor initially affiliates with hospital1 - vm.prank(hospital1); - registry.approveAffiliation(doctor1); - - vm.prank(doctor1); - registry.registerProfessional(hospital1); - - vm.prank(admin); - registry.verifyProfessional(doctor1); - - // Doctor changes affiliation to hospital2 - vm.prank(hospital2); - registry.approveAffiliation(doctor1); - - vm.prank(doctor1); - registry.changeAffiliation(hospital2); - - // Verify affiliation change - MedFiRegistry.Professional memory professional = registry.getProfessional(doctor1); - assertEq(professional.affiliatedHospital, hospital2); - assertTrue(professional.isAffiliated); - assertFalse(professional.isVerified); // Should lose verification - assertTrue(professional.isActive); - - // Verify old hospital no longer has professional - assertFalse(registry.isProfessionalAffiliated(hospital1, doctor1)); - assertTrue(registry.isProfessionalAffiliated(hospital2, doctor1)); + assertEq(avgRating, 4); } function testBlacklistingImpactOnExistingBookings() public { _setupVerifiedDoctor(); - // Patient approves USDC for subscription vm.prank(patient1); usdc.approve(address(records), STORAGE_FEE * 10); - // Patient grants access vm.prank(patient1); records.grantAccess(doctor1); - // Create booking uint256 bookingAmount = 100 * 10 ** 6; vm.prank(patient1); usdc.approve(address(escrow), bookingAmount); @@ -194,16 +144,13 @@ contract MedFiIntegrationTest is Test { vm.prank(patient1); escrow.bookService(doctor1, bookingAmount); - // Blacklist doctor vm.prank(admin); registry.blacklistProfessional(doctor1); - // Patient should not be able to confirm service with blacklisted doctor vm.prank(patient1); vm.expectRevert(MedFiEscrow.ProviderCannotPerformActivities.selector); escrow.confirmService(1); - // But owner can emergency refund vm.prank(owner); escrow.emergencyRefund(1); @@ -211,54 +158,30 @@ contract MedFiIntegrationTest is Test { } function testPatientAccessManagement() public { - _setupTwoHospitals(); + _setupTwoDoctors(); - // Setup two doctors from different hospitals - vm.prank(hospital1); - registry.approveAffiliation(doctor1); - vm.prank(hospital2); - registry.approveAffiliation(doctor2); - - vm.prank(doctor1); - registry.registerProfessional(hospital1); - vm.prank(doctor2); - registry.registerProfessional(hospital2); - - vm.prank(admin); - registry.verifyProfessional(doctor1); - vm.prank(admin); - registry.verifyProfessional(doctor2); - - // Patient approves USDC for subscription vm.prank(patient1); usdc.approve(address(records), STORAGE_FEE * 10); - // Patient grants access to doctor1 only vm.prank(patient1); records.grantAccess(doctor1); - // Verify access states assertTrue(records.hasAccess(patient1, doctor1)); assertFalse(records.hasAccess(patient1, doctor2)); - // Doctor1 can store records vm.prank(doctor1); records.storeRecordByCareProvider(patient1, "QmCardiacRecord"); - // Doctor2 cannot store records vm.prank(doctor2); vm.expectRevert(MedFiRecords.AccessNotGranted.selector); records.storeRecordByCareProvider(patient1, "QmNeuroRecord"); - // Grant access to doctor2 vm.prank(patient1); records.grantAccess(doctor2); - // Now doctor2 can store records vm.prank(doctor2); records.storeRecordByCareProvider(patient1, "QmNeuroRecord"); - // Both doctors can view patient records vm.prank(doctor1); MedFiRecords.MedicalRecord[] memory records1 = records.getPatientMedicalRecords(patient1); assertEq(records1.length, 2); @@ -335,7 +258,6 @@ contract MedFiIntegrationTest is Test { usdc.mint(patient3, ONE_USDC_AMOUNT); - // Renew subscription first vm.prank(patient3); records.renewSubscription(); @@ -388,7 +310,7 @@ contract MedFiIntegrationTest is Test { zeroFeeEscrow.bookService(doctor1, bookingAmount); MedFiEscrow.Booking memory booking = zeroFeeEscrow.getBooking(1); - assertEq(booking.amount, bookingAmount); // No fee deducted + assertEq(booking.amount, bookingAmount); assertEq(booking.originalAmount, bookingAmount); assertEq(zeroFeeEscrow.collectedFees(), 0); } @@ -415,40 +337,17 @@ contract MedFiIntegrationTest is Test { } function testComplexWorkflow() public { - // 1. Setup multiple hospitals and doctors - _setupTwoHospitals(); - - // 2. Register doctors with different hospitals - vm.prank(hospital1); - registry.approveAffiliation(doctor1); - - vm.prank(hospital2); - registry.approveAffiliation(doctor2); - - vm.prank(doctor1); - registry.registerProfessional(hospital1); + _setupTwoDoctors(); - vm.prank(doctor2); - registry.registerProfessional(hospital2); - - vm.prank(admin); - registry.verifyProfessional(doctor1); - - vm.prank(admin); - registry.verifyProfessional(doctor2); - - // 3. Patient approves USDC for all operations vm.prank(patient1); usdc.approve(address(records), STORAGE_FEE * 10); - // 4. Patient grants access to both doctors vm.prank(patient1); records.grantAccess(doctor1); vm.prank(patient1); records.grantAccess(doctor2); - // 5. Patient books with both doctors uint256 booking1Amount = 150 * 10 ** 6; uint256 booking2Amount = 250 * 10 ** 6; @@ -461,21 +360,18 @@ contract MedFiIntegrationTest is Test { vm.prank(patient1); escrow.bookService(doctor2, booking2Amount); - // 6. Doctors add records vm.prank(doctor1); records.storeRecordByCareProvider(patient1, "QmCardiacRecord"); vm.prank(doctor2); records.storeRecordByCareProvider(patient1, "QmNeuroRecord"); - // 7. Patient confirms both services vm.prank(patient1); escrow.confirmService(1); vm.prank(patient1); escrow.confirmService(2); - // 8. Patient rates both doctors vm.prank(patient1); records.rateHealthWorker(doctor1, 5); @@ -503,33 +399,6 @@ contract MedFiIntegrationTest is Test { assertEq(doctor2Records.length, 2); } - function testBatchOperations() public { - _setupTwoHospitals(); - - address[] memory doctorsToApprove = new address[](3); - doctorsToApprove[0] = makeAddr("batchDoctor1"); - doctorsToApprove[1] = makeAddr("batchDoctor2"); - doctorsToApprove[2] = makeAddr("batchDoctor3"); - - // Batch approve affiliations - vm.prank(hospital1); - registry.approveMultipleAffiliations(doctorsToApprove); - - // Verify all are approved - for (uint256 i = 0; i < doctorsToApprove.length; i++) { - assertTrue(registry.isAffiliationApproved(hospital1, doctorsToApprove[i])); - } - - // Batch revoke affiliations - vm.prank(hospital1); - registry.revokeMultipleAffiliationApprovals(doctorsToApprove); - - // Verify all are revoked - for (uint256 i = 0; i < doctorsToApprove.length; i++) { - assertFalse(registry.isAffiliationApproved(hospital1, doctorsToApprove[i])); - } - } - function testUSDCBatchMinting() public { address[] memory recipients = new address[](3); uint256[] memory amounts = new uint256[](3); @@ -553,34 +422,25 @@ contract MedFiIntegrationTest is Test { // ===== HELPER FUNCTIONS ===== function _setupVerifiedDoctor() internal { - vm.prank(hospital1); - registry.registerHospital(); - - vm.prank(admin); - registry.verifyHospital(hospital1); - - vm.prank(hospital1); - registry.approveAffiliation(doctor1); - vm.prank(doctor1); - registry.registerProfessional(hospital1); + registry.registerProfessional(); vm.prank(admin); registry.verifyProfessional(doctor1); } - function _setupTwoHospitals() internal { - vm.prank(hospital1); - registry.registerHospital(); + function _setupTwoDoctors() internal { + vm.prank(doctor1); + registry.registerProfessional(); - vm.prank(hospital2); - registry.registerHospital(); + vm.prank(doctor2); + registry.registerProfessional(); vm.prank(admin); - registry.verifyHospital(hospital1); + registry.verifyProfessional(doctor1); vm.prank(admin); - registry.verifyHospital(hospital2); + registry.verifyProfessional(doctor2); } function _createConfirmedBooking() internal { diff --git a/test/MedFiTest.t.sol b/test/MedFiTest.t.sol index d35d4b0..a0e3265 100644 --- a/test/MedFiTest.t.sol +++ b/test/MedFiTest.t.sol @@ -7,6 +7,7 @@ import "../src/MedFiEscrow.sol"; import "../src/MedFiRecords.sol"; import "./mock/MockUSDC.sol"; +// ===== FILE 1: MedFiTest.sol ===== contract MedFiTest is Test { // Contract instances MedFiRegistry public registry; @@ -30,19 +31,15 @@ contract MedFiTest is Test { uint256 public constant STORAGE_FEE = 1 * 10 ** 6; // 1 USDC function setUp() public { - // Start impersonating owner for deployment vm.startPrank(owner); - // Deploy contracts registry = new MedFiRegistry(); usdc = new MockUSDC(); escrow = new MedFiEscrow(address(registry), address(usdc), FEE_PERCENT); records = new MedFiRecords(address(registry), address(escrow), address(usdc)); - // Add admin registry.addAdmin(admin); - // Give test USDC to patients usdc.mint(patient1, INITIAL_USDC_AMOUNT); usdc.mint(patient2, INITIAL_USDC_AMOUNT); @@ -56,57 +53,60 @@ contract MedFiTest is Test { registry.registerHospital(); - (bool verified, uint256 timestamp) = registry.getHospital(hospital1); + (bool verified, uint256 timestamp, bool active) = registry.getHospital(hospital1); assertFalse(verified); assertGt(timestamp, 0); + assertTrue(active); vm.stopPrank(); } function testHospitalVerification() public { - // Register hospital first vm.prank(hospital1); registry.registerHospital(); - // Verify hospital as admin vm.prank(admin); registry.verifyHospital(hospital1); - (bool verified,) = registry.getHospital(hospital1); + (bool verified,, bool active) = registry.getHospital(hospital1); assertTrue(verified); + assertTrue(active); } - function testProfessionalRegistration() public { - // Setup: Register and verify hospital + function testHospitalBlacklisting() public { vm.prank(hospital1); registry.registerHospital(); vm.prank(admin); registry.verifyHospital(hospital1); - // Hospital approves affiliation - vm.prank(hospital1); - registry.approveAffiliation(doctor1); + assertTrue(registry.canPerformActivities(hospital1)); - // Doctor registers + vm.prank(admin); + registry.blacklistHospital(hospital1); + + assertFalse(registry.canPerformActivities(hospital1)); + assertTrue(registry.isBlacklisted(hospital1)); + } + + function testProfessionalRegistration() public { vm.prank(doctor1); - registry.registerProfessional(hospital1); + registry.registerProfessional(); MedFiRegistry.Professional memory professional = registry.getProfessional(doctor1); - assertEq(professional.affiliatedHospital, hospital1); - assertTrue(professional.isAffiliated); - assertFalse(professional.isVerified); // Not verified by admin yet + assertEq(professional.professionalAddress, doctor1); + assertFalse(professional.isVerified); + assertTrue(professional.isActive); } function testProfessionalVerification() public { _setupVerifiedDoctor(); - // Verify professional vm.prank(admin); registry.verifyProfessional(doctor1); - assertTrue(registry.isVerified(doctor1)); + assertTrue(registry.isVerifiedProfessional(doctor1)); assertTrue(registry.canPerformActivities(doctor1)); } @@ -118,7 +118,6 @@ contract MedFiTest is Test { assertTrue(registry.canPerformActivities(doctor1)); - // Blacklist professional vm.prank(admin); registry.blacklistProfessional(doctor1); @@ -137,7 +136,7 @@ contract MedFiTest is Test { function testUSDCFaucet() public { address newUser = makeAddr("newUser"); - uint256 faucetAmount = 100 * 10 ** 6; // 100 USDC + uint256 faucetAmount = 100 * 10 ** 6; vm.prank(newUser); usdc.faucet(faucetAmount); @@ -161,17 +160,14 @@ contract MedFiTest is Test { vm.prank(admin); registry.verifyProfessional(doctor1); - uint256 bookingAmount = 100 * 10 ** 6; // 100 USDC + uint256 bookingAmount = 100 * 10 ** 6; - // Patient approves USDC spending vm.prank(patient1); usdc.approve(address(escrow), bookingAmount); - // Book service vm.prank(patient1); escrow.bookService(doctor1, bookingAmount); - // Check booking details MedFiEscrow.Booking memory booking = escrow.getBooking(1); assertEq(booking.patient, patient1); assertEq(booking.provider, doctor1); @@ -179,7 +175,6 @@ contract MedFiTest is Test { assertTrue(booking.isPaid); assertFalse(booking.confirmed); - // Check fee calculation uint256 expectedFee = (bookingAmount * FEE_PERCENT) / 100; uint256 expectedAmount = bookingAmount - expectedFee; assertEq(booking.amount, expectedAmount); @@ -191,19 +186,15 @@ contract MedFiTest is Test { uint256 doctorBalanceBefore = usdc.balanceOf(doctor1); - // Confirm service vm.prank(patient1); escrow.confirmService(1); - // Check booking status MedFiEscrow.Booking memory booking = escrow.getBooking(1); assertTrue(booking.confirmed); - // Check doctor received payment uint256 doctorBalanceAfter = usdc.balanceOf(doctor1); assertEq(doctorBalanceAfter, doctorBalanceBefore + booking.amount); - // Check provider stats (uint256 confirmedBookings, uint256 totalEarnings, bool canReceive) = escrow.getProviderStats(doctor1); assertEq(confirmedBookings, 1); assertEq(totalEarnings, booking.amount); @@ -216,15 +207,12 @@ contract MedFiTest is Test { uint256 patientBalanceBefore = usdc.balanceOf(patient1); MedFiEscrow.Booking memory bookingBefore = escrow.getBooking(1); - // Provider refunds patient vm.prank(doctor1); escrow.refundPatient(1); - // Check booking status MedFiEscrow.Booking memory booking = escrow.getBooking(1); assertFalse(booking.isPaid); - // Check patient received refund uint256 patientBalanceAfter = usdc.balanceOf(patient1); assertEq(patientBalanceAfter, patientBalanceBefore + bookingBefore.amount); } @@ -235,11 +223,9 @@ contract MedFiTest is Test { uint256 patientBalanceBefore = usdc.balanceOf(patient1); MedFiEscrow.Booking memory bookingBefore = escrow.getBooking(1); - // Owner performs emergency refund vm.prank(owner); escrow.emergencyRefund(1); - // Check patient received refund uint256 patientBalanceAfter = usdc.balanceOf(patient1); assertEq(patientBalanceAfter, patientBalanceBefore + bookingBefore.amount); } @@ -250,11 +236,9 @@ contract MedFiTest is Test { uint256 ownerBalanceBefore = usdc.balanceOf(owner); uint256 collectedFees = escrow.collectedFees(); - // Withdraw fees vm.prank(owner); escrow.withdrawFees(); - // Check owner received fees uint256 ownerBalanceAfter = usdc.balanceOf(owner); assertEq(ownerBalanceAfter, ownerBalanceBefore + collectedFees); assertEq(escrow.collectedFees(), 0); @@ -265,7 +249,6 @@ contract MedFiTest is Test { vm.prank(admin); registry.verifyProfessional(doctor1); - // Blacklist doctor vm.prank(admin); registry.blacklistProfessional(doctor1); @@ -273,7 +256,6 @@ contract MedFiTest is Test { vm.prank(patient1); usdc.approve(address(escrow), bookingAmount); - // Should revert when trying to book vm.prank(patient1); vm.expectRevert(MedFiEscrow.ProviderCannotPerformActivities.selector); escrow.bookService(doctor1, bookingAmount); @@ -286,36 +268,28 @@ contract MedFiTest is Test { vm.prank(admin); registry.verifyProfessional(doctor1); - // Patient approves USDC for subscription vm.prank(patient1); usdc.approve(address(records), STORAGE_FEE); - // Patient grants access to doctor (will trigger subscription payment) vm.prank(patient1); records.grantAccess(doctor1); - // Check access granted assertTrue(records.hasAccess(patient1, doctor1)); assertTrue(records.patientAccessGrants(patient1, doctor1)); - - // Check subscription is active assertTrue(records.isSubscriptionActive(patient1)); } function testRevokeAccess() public { _setupVerifiedDoctorWithSubscription(); - // Grant access first vm.prank(patient1); records.grantAccess(doctor1); assertTrue(records.hasAccess(patient1, doctor1)); - // Revoke access vm.prank(patient1); records.revokeAccess(doctor1); - // Check access revoked assertFalse(records.hasAccess(patient1, doctor1)); assertFalse(records.patientAccessGrants(patient1, doctor1)); } @@ -323,11 +297,9 @@ contract MedFiTest is Test { function testCannotGrantAccessToBlacklistedProfessional() public { _setupVerifiedDoctorWithSubscription(); - // Blacklist doctor vm.prank(admin); registry.blacklistProfessional(doctor1); - // Try to grant access - should revert vm.prank(patient1); vm.expectRevert(MedFiRecords.CannotPerformActivities.selector); records.grantAccess(doctor1); @@ -336,17 +308,14 @@ contract MedFiTest is Test { function testStoreRecordByCareProviderWithAccess() public { _setupVerifiedDoctorWithSubscription(); - // Grant access to doctor vm.prank(patient1); records.grantAccess(doctor1); string memory recordHash = "QmTestHash123"; - // Doctor stores record vm.prank(doctor1); records.storeRecordByCareProvider(patient1, recordHash); - // Check professional stats - Note: profession is stored off-chain now (uint256 recordCount, bool isActive) = records.getProfessionalStats(doctor1); assertEq(recordCount, 1); assertTrue(isActive); @@ -355,17 +324,14 @@ contract MedFiTest is Test { function testCannotStoreRecordWithoutAccess() public { _setupVerifiedDoctorWithSubscription(); - // Don't grant access string memory recordHash = "QmTestHash123"; - // Try to store record - should revert vm.prank(doctor1); vm.expectRevert(MedFiRecords.AccessNotGranted.selector); records.storeRecordByCareProvider(patient1, recordHash); } function testPatientCanAddOwnMedicalRecord() public { - // Patient approves USDC for subscription vm.prank(patient1); usdc.approve(address(records), STORAGE_FEE); @@ -374,7 +340,6 @@ contract MedFiTest is Test { vm.prank(patient1); records.addMedicalRecord(recordHash); - // Check patient can view their own records vm.prank(patient1); MedFiRecords.MedicalRecord[] memory patientRecords = records.getMyMedicalRecords(); assertEq(patientRecords.length, 1); @@ -386,7 +351,6 @@ contract MedFiTest is Test { function testPatientCanViewOwnRecords() public { _setupVerifiedDoctorWithSubscription(); - // Grant access and add records vm.prank(patient1); records.grantAccess(doctor1); @@ -396,7 +360,6 @@ contract MedFiTest is Test { vm.prank(doctor1); records.storeRecordByCareProvider(patient1, "QmDoctorRecord1"); - // Patient can view all their records vm.prank(patient1); MedFiRecords.MedicalRecord[] memory patientRecords = records.getMyMedicalRecords(); assertEq(patientRecords.length, 2); @@ -405,14 +368,12 @@ contract MedFiTest is Test { function testAuthorizedProfessionalCanViewPatientRecords() public { _setupVerifiedDoctorWithSubscription(); - // Grant access and add records vm.prank(patient1); records.grantAccess(doctor1); vm.prank(patient1); records.addMedicalRecord("QmPatientRecord1"); - // Doctor can view patient's records vm.prank(doctor1); MedFiRecords.MedicalRecord[] memory patientRecords = records.getPatientMedicalRecords(patient1); assertEq(patientRecords.length, 1); @@ -422,11 +383,9 @@ contract MedFiTest is Test { function testUnauthorizedProfessionalCannotViewPatientRecords() public { _setupVerifiedDoctorWithSubscription(); - // Don't grant access vm.prank(patient1); records.addMedicalRecord("QmPatientRecord1"); - // Doctor cannot view patient's records without permission vm.prank(doctor1); vm.expectRevert(MedFiRecords.AccessNotGranted.selector); records.getPatientMedicalRecords(patient1); @@ -435,38 +394,30 @@ contract MedFiTest is Test { function testManualSubscriptionRenewal() public { _setupVerifiedDoctorWithSubscription(); - // Grant access (first subscription) vm.prank(patient1); records.grantAccess(doctor1); - // Approve for manual renewal vm.prank(patient1); usdc.approve(address(records), STORAGE_FEE); - // Manually renew subscription vm.prank(patient1); records.renewSubscription(); - // Check subscription is still active assertTrue(records.isSubscriptionActive(patient1)); } function testSubscriptionExpiry() public { _setupVerifiedDoctorWithSubscription(); - // Grant access (pay subscription) vm.prank(patient1); records.grantAccess(doctor1); - // Fast forward past subscription period vm.warp(block.timestamp + 31 days); - // Subscription should be expired assertFalse(records.isSubscriptionActive(patient1)); } function testRateHealthWorker() public { - // Setup confirmed booking first _setupBooking(); vm.prank(patient1); escrow.confirmService(1); @@ -475,7 +426,6 @@ contract MedFiTest is Test { vm.prank(patient1); records.rateHealthWorker(doctor1, rating); - // Check rating uint256 averageRating = records.getAverageRating(doctor1); assertEq(averageRating, rating); @@ -493,7 +443,6 @@ contract MedFiTest is Test { vm.prank(patient1); records.rateHealthWorker(doctor1, 5); - // Try to rate again - should revert vm.prank(patient1); vm.expectRevert(MedFiRecords.AlreadyRated.selector); records.rateHealthWorker(doctor1, 4); @@ -504,7 +453,6 @@ contract MedFiTest is Test { vm.prank(admin); registry.verifyProfessional(doctor1); - // Try to rate without confirmed booking - should revert vm.prank(patient1); vm.expectRevert(MedFiRecords.ExceedsConfirmedBookings.selector); records.rateHealthWorker(doctor1, 5); @@ -513,20 +461,8 @@ contract MedFiTest is Test { // ===== HELPER FUNCTIONS ===== function _setupVerifiedDoctor() internal { - // Register and verify hospital - vm.prank(hospital1); - registry.registerHospital(); - - vm.prank(admin); - registry.verifyHospital(hospital1); - - // Hospital approves affiliation - vm.prank(hospital1); - registry.approveAffiliation(doctor1); - - // Doctor registers vm.prank(doctor1); - registry.registerProfessional(hospital1); + registry.registerProfessional(); } function _setupVerifiedDoctorWithSubscription() internal { @@ -534,9 +470,8 @@ contract MedFiTest is Test { vm.prank(admin); registry.verifyProfessional(doctor1); - // Patient approves USDC for subscription vm.prank(patient1); - usdc.approve(address(records), STORAGE_FEE * 10); // Approve for multiple operations + usdc.approve(address(records), STORAGE_FEE * 10); } function _setupBooking() internal { @@ -544,7 +479,7 @@ contract MedFiTest is Test { vm.prank(admin); registry.verifyProfessional(doctor1); - uint256 bookingAmount = 100 * 10 ** 6; // 100 USDC + uint256 bookingAmount = 100 * 10 ** 6; vm.prank(patient1); usdc.approve(address(escrow), bookingAmount);