import unittest
from dataclasses import replace
from approval_gate import Draft, ApprovalGate

class ApprovalTests(unittest.TestCase):
    def setUp(self):
        self.gate=ApprovalGate({'reviewer-1'})
        self.draft=Draft('sample-1','Example title','Synthetic body','private-test')
        self.calls=[]
    def publish(self,draft):
        self.calls.append(draft)
        return 'fake-publication-1'
    def approve(self):
        self.gate.review(self.draft,'reviewer-1',True,100,20)
    def blocked(self,draft=None,now=101):
        with self.assertRaises(PermissionError):
            self.gate.publish(draft or self.draft,now,self.publish)
        self.assertEqual(self.calls,[])
    def test_no_decision_cannot_publish(self):self.blocked()
    def test_rejection_cannot_publish(self):
        self.gate.review(self.draft,'reviewer-1',False,100,20);self.blocked()
    def test_changed_body_cannot_publish(self):
        self.approve();self.blocked(replace(self.draft,body='Changed'))
    def test_changed_destination_cannot_publish(self):
        self.approve();self.blocked(replace(self.draft,destination='public'))
    def test_expired_cannot_publish(self):
        self.approve();self.blocked(now=120)
    def test_wrong_reviewer_cannot_approve(self):
        with self.assertRaises(PermissionError):
            self.gate.review(self.draft,'unknown',True,100,20)
        self.blocked()
    def test_valid_approval_publishes_once(self):
        self.approve()
        self.assertEqual(self.gate.publish(self.draft,101,self.publish),'fake-publication-1')
        with self.assertRaises(PermissionError):self.gate.publish(self.draft,102,self.publish)
        self.assertEqual(self.calls,[self.draft])
    def test_duplicate_approval_cannot_reenable(self):
        self.approve();self.gate.publish(self.draft,101,self.publish)
        with self.assertRaises(PermissionError):self.approve()
        self.assertEqual(len(self.calls),1)
    def test_uncertain_write_cannot_retry_automatically(self):
        self.approve()
        def fail(draft):raise TimeoutError('Synthetic lost response')
        with self.assertRaises(TimeoutError):self.gate.publish(self.draft,101,fail)
        self.blocked(now=102)

if __name__=='__main__':unittest.main(verbosity=2)
