File size: 3,148 Bytes
7e25f7a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
"""Unit tests for cores.metadata.forensic — ICC, thumbnail, editing history, etc."""

from __future__ import annotations

import io

import pytest
from PIL import Image

from cores.metadata import (
    extract_icc_profile,
    extract_thumbnail,
    extract_embedded_preview,
    extract_editing_history,
    analyze_compression,
    normalize_timestamp,
    extract_lens_info,
    camera_fingerprint,
)


class TestICCProfile:
    def test_no_icc_returns_none(self, sample_image_bytes):
        result = extract_icc_profile(sample_image_bytes)
        # Synthetic JPEG likely has no ICC
        assert result is None or result.get("present") is False

    def test_empty_bytes(self):
        assert extract_icc_profile(b"") is None


class TestThumbnail:
    def test_no_thumbnail_returns_none(self, sample_image_bytes):
        result = extract_thumbnail(sample_image_bytes)
        assert result is None or result.get("present") is False

    def test_empty_bytes(self):
        assert extract_thumbnail(b"") is None


class TestEditingHistory:
    def test_empty_exif(self):
        assert extract_editing_history({}) == []

    def test_extracts_software(self):
        exif = {"Software": "Adobe Photoshop"}
        history = extract_editing_history(exif)
        assert any("Software" in h for h in history)

    def test_extracts_artist(self):
        exif = {"Artist": "John Doe"}
        history = extract_editing_history(exif)
        assert any("Artist" in h for h in history)


class TestCompressionAnalysis:
    def test_returns_format(self, sample_image_bytes):
        result = analyze_compression(sample_image_bytes)
        assert result is not None
        assert result["format"] == "JPEG"
        assert result["size_bytes"] > 0
        assert result["compression_ratio"] > 0

    def test_empty_bytes(self):
        assert analyze_compression(b"") is None


class TestTimestampNormalization:
    def test_exif_format(self):
        result = normalize_timestamp("2023:01:15 14:30:45")
        assert result is not None
        assert result["iso"].startswith("2023-01-15T14:30:45")
        assert result["timezone"] == "unknown"

    def test_iso_format(self):
        result = normalize_timestamp("2023-01-15 14:30:45")
        assert result is not None
        assert result["iso"].startswith("2023-01-15")

    def test_empty(self):
        assert normalize_timestamp("") is None

    def test_invalid(self):
        result = normalize_timestamp("not a date")
        assert result is not None
        assert result["iso"] is None


class TestLensInfo:
    def test_extracts_lens_model(self):
        exif = {"LensModel": "24-70mm f/2.8"}
        assert extract_lens_info(exif) == "24-70mm f/2.8"

    def test_empty_exif(self):
        assert extract_lens_info({}) is None


class TestCameraFingerprint:
    def test_returns_hash(self, sample_image_bytes):
        fp = camera_fingerprint(sample_image_bytes, (200, 200))
        # Should return a 32-char hash or None
        if fp is not None:
            assert len(fp) == 32

    def test_empty_bytes(self):
        assert camera_fingerprint(b"", (200, 200)) is None