import hashlib
import tempfile
import unittest
from pathlib import Path
from manifest_validator import parse_sha256_manifest, verify_manifest


class ManifestTests(unittest.TestCase):
    def setUp(self):
        self.tmp = tempfile.TemporaryDirectory()
        self.addCleanup(self.tmp.cleanup)
        self.root = Path(self.tmp.name) / 'release'
        self.root.mkdir()
        (self.root/'sample.txt').write_bytes(b'sample')
        self.digest = hashlib.sha256(b'sample').hexdigest()
        self.manifest = self.root/'SHA256SUMS'

    def record(self, name='sample.txt', digest=None):
        self.manifest.write_text((digest or self.digest) + '  ' + name + '\n', encoding='utf-8')

    def test_valid_file(self):
        self.record()
        self.assertEqual(list(verify_manifest(self.manifest).values()), ['OK'])

    def test_bad_checksum(self):
        self.record(digest='0'*64)
        self.assertEqual(list(verify_manifest(self.manifest).values()), ['FAILED_CHECKSUM'])

    def test_missing_file(self):
        self.record('missing.txt')
        self.assertEqual(list(verify_manifest(self.manifest).values()), ['MISSING'])

    def test_unsafe_paths(self):
        for name in ('../outside', '/etc/passwd', 'C:/outside', '..\\outside', './sample.txt', 'nested//sample.txt'):
            with self.subTest(name=name):
                self.record(name)
                with self.assertRaises(ValueError):
                    parse_sha256_manifest(self.manifest)

    def test_duplicate(self):
        self.record()
        self.manifest.write_text(self.manifest.read_text()*2)
        with self.assertRaises(ValueError):
            parse_sha256_manifest(self.manifest)

    def test_symlink_outside(self):
        outside = Path(self.tmp.name)/'outside.txt'
        outside.write_bytes(b'sample')
        try:
            (self.root/'link.txt').symlink_to(outside)
        except OSError as exc:
            self.skipTest(f'Symlink creation unavailable: {exc}')
        self.record('link.txt')
        with self.assertRaises(ValueError):
            parse_sha256_manifest(self.manifest)

    def test_empty_and_invalid_manifest(self):
        for content in ('', '# comments only\n', 'not a digest\n', 'z'*64+'  sample.txt\n'):
            with self.subTest(content=content):
                self.manifest.write_text(content)
                with self.assertRaises(ValueError):
                    parse_sha256_manifest(self.manifest)


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