All checks were successful
Python CI / test (push) Successful in 1m20s
Lint / lint (push) Successful in 1m4s
Tests / test (3.11) (push) Successful in 1m27s
Tests / test (3.12) (push) Successful in 2m25s
Tests / test (3.13) (push) Successful in 2m52s
Tests / test (3.14) (push) Successful in 1m9s
512 lines
19 KiB
Python
512 lines
19 KiB
Python
"""
|
|
Tests for AutosaveManager
|
|
"""
|
|
|
|
import pytest
|
|
import json
|
|
import tempfile
|
|
import shutil
|
|
from pathlib import Path
|
|
from datetime import datetime, timedelta
|
|
from unittest.mock import Mock, patch, MagicMock
|
|
|
|
from pyPhotoAlbum.autosave_manager import AutosaveManager
|
|
|
|
|
|
class TestAutosaveManagerInit:
|
|
"""Tests for AutosaveManager initialization"""
|
|
|
|
def test_init_creates_checkpoint_directory(self, tmp_path, monkeypatch):
|
|
"""Test that init creates the checkpoint directory"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
|
|
assert checkpoint_dir.exists()
|
|
|
|
def test_init_with_existing_directory(self, tmp_path, monkeypatch):
|
|
"""Test init when checkpoint directory already exists"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
|
|
assert checkpoint_dir.exists()
|
|
|
|
|
|
class TestGetCheckpointPath:
|
|
"""Tests for _get_checkpoint_path method"""
|
|
|
|
def test_get_checkpoint_path_basic(self, tmp_path, monkeypatch):
|
|
"""Test basic checkpoint path generation"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
path = manager._get_checkpoint_path("MyProject")
|
|
|
|
assert path.parent == checkpoint_dir
|
|
assert path.suffix == ".ppz"
|
|
assert "checkpoint_MyProject_" in path.name
|
|
|
|
def test_get_checkpoint_path_with_timestamp(self, tmp_path, monkeypatch):
|
|
"""Test checkpoint path with specific timestamp"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
timestamp = datetime(2024, 1, 15, 10, 30, 45)
|
|
path = manager._get_checkpoint_path("TestProject", timestamp)
|
|
|
|
assert "20240115_103045" in path.name
|
|
|
|
def test_get_checkpoint_path_sanitizes_name(self, tmp_path, monkeypatch):
|
|
"""Test that special characters in project name are sanitized"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
path = manager._get_checkpoint_path("My Project!@#$%")
|
|
|
|
# Should not contain special characters except - and _
|
|
name_without_ext = path.stem
|
|
for char in name_without_ext:
|
|
assert char.isalnum() or char in "-_", f"Invalid char: {char}"
|
|
|
|
|
|
class TestCreateCheckpoint:
|
|
"""Tests for create_checkpoint method"""
|
|
|
|
def test_create_checkpoint_success(self, tmp_path, monkeypatch):
|
|
"""Test successful checkpoint creation"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
|
|
# Mock save_to_zip - note the return value format
|
|
with patch("pyPhotoAlbum.autosave_manager.save_to_zip") as mock_save:
|
|
mock_save.return_value = (True, "Success")
|
|
|
|
mock_project = Mock()
|
|
mock_project.name = "TestProject"
|
|
mock_project.file_path = "/path/to/project.ppz"
|
|
|
|
success, message = manager.create_checkpoint(mock_project)
|
|
|
|
assert success is True
|
|
assert "Checkpoint created" in message
|
|
mock_save.assert_called_once()
|
|
|
|
def test_create_checkpoint_failure(self, tmp_path, monkeypatch):
|
|
"""Test checkpoint creation failure"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
|
|
with patch("pyPhotoAlbum.autosave_manager.save_to_zip") as mock_save:
|
|
mock_save.return_value = (False, "Disk full")
|
|
|
|
mock_project = Mock()
|
|
mock_project.name = "TestProject"
|
|
|
|
success, message = manager.create_checkpoint(mock_project)
|
|
|
|
assert success is False
|
|
assert "Checkpoint failed" in message
|
|
|
|
def test_create_checkpoint_exception(self, tmp_path, monkeypatch):
|
|
"""Test checkpoint creation with exception"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
|
|
with patch("pyPhotoAlbum.autosave_manager.save_to_zip") as mock_save:
|
|
mock_save.side_effect = Exception("IO Error")
|
|
|
|
mock_project = Mock()
|
|
mock_project.name = "TestProject"
|
|
|
|
success, message = manager.create_checkpoint(mock_project)
|
|
|
|
assert success is False
|
|
assert "Checkpoint error" in message
|
|
|
|
|
|
class TestSaveCheckpointMetadata:
|
|
"""Tests for _save_checkpoint_metadata method"""
|
|
|
|
def test_save_metadata(self, tmp_path, monkeypatch):
|
|
"""Test saving checkpoint metadata"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
|
|
mock_project = Mock()
|
|
mock_project.name = "TestProject"
|
|
mock_project.file_path = "/path/to/original.ppz"
|
|
|
|
checkpoint_path = checkpoint_dir / "checkpoint_TestProject_20240115_103045.ppz"
|
|
checkpoint_path.touch()
|
|
|
|
manager._save_checkpoint_metadata(mock_project, checkpoint_path)
|
|
|
|
metadata_path = checkpoint_path.with_suffix(".json")
|
|
assert metadata_path.exists()
|
|
|
|
with open(metadata_path, "r") as f:
|
|
metadata = json.load(f)
|
|
|
|
assert metadata["project_name"] == "TestProject"
|
|
assert metadata["original_path"] == "/path/to/original.ppz"
|
|
assert "timestamp" in metadata
|
|
|
|
|
|
class TestListCheckpoints:
|
|
"""Tests for list_checkpoints method"""
|
|
|
|
def test_list_checkpoints_empty(self, tmp_path, monkeypatch):
|
|
"""Test listing checkpoints when none exist"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
checkpoints = manager.list_checkpoints()
|
|
|
|
assert checkpoints == []
|
|
|
|
def test_list_checkpoints_with_files(self, tmp_path, monkeypatch):
|
|
"""Test listing checkpoints with existing files"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
# Create some checkpoint files
|
|
cp1 = checkpoint_dir / "checkpoint_Project1_20240115_100000.ppz"
|
|
cp2 = checkpoint_dir / "checkpoint_Project2_20240115_110000.ppz"
|
|
cp1.touch()
|
|
cp2.touch()
|
|
|
|
# Create metadata for first checkpoint
|
|
metadata1 = {"project_name": "Project1", "timestamp": "2024-01-15T10:00:00"}
|
|
with open(cp1.with_suffix(".json"), "w") as f:
|
|
json.dump(metadata1, f)
|
|
|
|
manager = AutosaveManager()
|
|
checkpoints = manager.list_checkpoints()
|
|
|
|
assert len(checkpoints) == 2
|
|
|
|
def test_list_checkpoints_filter_by_project(self, tmp_path, monkeypatch):
|
|
"""Test listing checkpoints filtered by project name"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
# Create checkpoint files with metadata
|
|
cp1 = checkpoint_dir / "checkpoint_Project1_20240115_100000.ppz"
|
|
cp2 = checkpoint_dir / "checkpoint_Project2_20240115_110000.ppz"
|
|
cp1.touch()
|
|
cp2.touch()
|
|
|
|
metadata1 = {"project_name": "Project1", "timestamp": "2024-01-15T10:00:00"}
|
|
metadata2 = {"project_name": "Project2", "timestamp": "2024-01-15T11:00:00"}
|
|
|
|
with open(cp1.with_suffix(".json"), "w") as f:
|
|
json.dump(metadata1, f)
|
|
with open(cp2.with_suffix(".json"), "w") as f:
|
|
json.dump(metadata2, f)
|
|
|
|
manager = AutosaveManager()
|
|
checkpoints = manager.list_checkpoints("Project1")
|
|
|
|
assert len(checkpoints) == 1
|
|
assert checkpoints[0][1]["project_name"] == "Project1"
|
|
|
|
def test_list_checkpoints_sorted_by_timestamp(self, tmp_path, monkeypatch):
|
|
"""Test that checkpoints are sorted by timestamp (newest first)"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
# Create checkpoints with different timestamps
|
|
cp1 = checkpoint_dir / "checkpoint_Project_20240115_080000.ppz"
|
|
cp2 = checkpoint_dir / "checkpoint_Project_20240115_120000.ppz"
|
|
cp3 = checkpoint_dir / "checkpoint_Project_20240115_100000.ppz"
|
|
cp1.touch()
|
|
cp2.touch()
|
|
cp3.touch()
|
|
|
|
for cp, hour in [(cp1, "08"), (cp2, "12"), (cp3, "10")]:
|
|
metadata = {"project_name": "Project", "timestamp": f"2024-01-15T{hour}:00:00"}
|
|
with open(cp.with_suffix(".json"), "w") as f:
|
|
json.dump(metadata, f)
|
|
|
|
manager = AutosaveManager()
|
|
checkpoints = manager.list_checkpoints()
|
|
|
|
# Should be sorted newest first: 12:00, 10:00, 08:00
|
|
assert "12:00:00" in checkpoints[0][1]["timestamp"]
|
|
assert "10:00:00" in checkpoints[1][1]["timestamp"]
|
|
assert "08:00:00" in checkpoints[2][1]["timestamp"]
|
|
|
|
|
|
class TestLoadCheckpoint:
|
|
"""Tests for load_checkpoint method"""
|
|
|
|
def test_load_checkpoint_success(self, tmp_path, monkeypatch):
|
|
"""Test successful checkpoint loading"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
|
|
with patch("pyPhotoAlbum.autosave_manager.load_from_zip") as mock_load:
|
|
mock_project = Mock()
|
|
mock_load.return_value = mock_project
|
|
|
|
checkpoint_path = checkpoint_dir / "checkpoint_Test.ppz"
|
|
success, result = manager.load_checkpoint(checkpoint_path)
|
|
|
|
assert success is True
|
|
assert result == mock_project
|
|
|
|
def test_load_checkpoint_failure(self, tmp_path, monkeypatch):
|
|
"""Test checkpoint loading failure"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
|
|
with patch("pyPhotoAlbum.autosave_manager.load_from_zip") as mock_load:
|
|
mock_load.side_effect = Exception("Corrupt file")
|
|
|
|
checkpoint_path = checkpoint_dir / "checkpoint_Test.ppz"
|
|
success, result = manager.load_checkpoint(checkpoint_path)
|
|
|
|
assert success is False
|
|
assert "Failed to load checkpoint" in result
|
|
|
|
|
|
class TestDeleteCheckpoint:
|
|
"""Tests for delete_checkpoint method"""
|
|
|
|
def test_delete_checkpoint_success(self, tmp_path, monkeypatch):
|
|
"""Test successful checkpoint deletion"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
# Create checkpoint and metadata files
|
|
cp = checkpoint_dir / "checkpoint_Test.ppz"
|
|
cp.touch()
|
|
metadata = cp.with_suffix(".json")
|
|
metadata.touch()
|
|
|
|
manager = AutosaveManager()
|
|
result = manager.delete_checkpoint(cp)
|
|
|
|
assert result is True
|
|
assert not cp.exists()
|
|
assert not metadata.exists()
|
|
|
|
def test_delete_checkpoint_nonexistent(self, tmp_path, monkeypatch):
|
|
"""Test deleting nonexistent checkpoint"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
cp = checkpoint_dir / "nonexistent.ppz"
|
|
result = manager.delete_checkpoint(cp)
|
|
|
|
assert result is True # Should succeed even if file doesn't exist
|
|
|
|
|
|
class TestDeleteAllCheckpoints:
|
|
"""Tests for delete_all_checkpoints method"""
|
|
|
|
def test_delete_all_checkpoints(self, tmp_path, monkeypatch):
|
|
"""Test deleting all checkpoints"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
# Create multiple checkpoints
|
|
for i in range(3):
|
|
cp = checkpoint_dir / f"checkpoint_Project_{i}.ppz"
|
|
cp.touch()
|
|
metadata = {"project_name": "Project", "timestamp": f"2024-01-15T{i}:00:00"}
|
|
with open(cp.with_suffix(".json"), "w") as f:
|
|
json.dump(metadata, f)
|
|
|
|
manager = AutosaveManager()
|
|
manager.delete_all_checkpoints()
|
|
|
|
remaining = list(checkpoint_dir.glob("checkpoint_*.ppz"))
|
|
assert len(remaining) == 0
|
|
|
|
def test_delete_all_checkpoints_filtered(self, tmp_path, monkeypatch):
|
|
"""Test deleting all checkpoints for specific project"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
# Create checkpoints for different projects
|
|
for name in ["ProjectA", "ProjectB", "ProjectA"]:
|
|
cp = checkpoint_dir / f"checkpoint_{name}_{datetime.now().strftime('%Y%m%d_%H%M%S%f')}.ppz"
|
|
cp.touch()
|
|
metadata = {"project_name": name, "timestamp": datetime.now().isoformat()}
|
|
with open(cp.with_suffix(".json"), "w") as f:
|
|
json.dump(metadata, f)
|
|
|
|
manager = AutosaveManager()
|
|
manager.delete_all_checkpoints("ProjectA")
|
|
|
|
# Only ProjectB should remain
|
|
remaining = list(checkpoint_dir.glob("checkpoint_*.ppz"))
|
|
assert len(remaining) == 1
|
|
assert "ProjectB" in remaining[0].name
|
|
|
|
|
|
class TestCleanupOldCheckpoints:
|
|
"""Tests for cleanup_old_checkpoints method"""
|
|
|
|
def test_cleanup_old_checkpoints_by_age(self, tmp_path, monkeypatch):
|
|
"""Test cleanup of old checkpoints by age"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
# Create old and new checkpoints
|
|
old_time = datetime.now() - timedelta(hours=48)
|
|
new_time = datetime.now() - timedelta(hours=1)
|
|
|
|
old_cp = checkpoint_dir / "checkpoint_Project_old.ppz"
|
|
new_cp = checkpoint_dir / "checkpoint_Project_new.ppz"
|
|
old_cp.touch()
|
|
new_cp.touch()
|
|
|
|
old_metadata = {"project_name": "Project", "timestamp": old_time.isoformat()}
|
|
new_metadata = {"project_name": "Project", "timestamp": new_time.isoformat()}
|
|
|
|
with open(old_cp.with_suffix(".json"), "w") as f:
|
|
json.dump(old_metadata, f)
|
|
with open(new_cp.with_suffix(".json"), "w") as f:
|
|
json.dump(new_metadata, f)
|
|
|
|
manager = AutosaveManager()
|
|
manager.cleanup_old_checkpoints(max_age_hours=24)
|
|
|
|
# Only new checkpoint should remain
|
|
remaining = list(checkpoint_dir.glob("checkpoint_*.ppz"))
|
|
assert len(remaining) == 1
|
|
assert "new" in remaining[0].name
|
|
|
|
def test_cleanup_old_checkpoints_by_count(self, tmp_path, monkeypatch):
|
|
"""Test cleanup of checkpoints by count"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
# Create many recent checkpoints
|
|
for i in range(5):
|
|
timestamp = datetime.now() - timedelta(hours=i)
|
|
cp = checkpoint_dir / f"checkpoint_Project_{i:02d}.ppz"
|
|
cp.touch()
|
|
metadata = {"project_name": "Project", "timestamp": timestamp.isoformat()}
|
|
with open(cp.with_suffix(".json"), "w") as f:
|
|
json.dump(metadata, f)
|
|
|
|
manager = AutosaveManager()
|
|
manager.cleanup_old_checkpoints(max_age_hours=24 * 7, max_count=3)
|
|
|
|
# Should only keep 3 most recent
|
|
remaining = list(checkpoint_dir.glob("checkpoint_*.ppz"))
|
|
assert len(remaining) == 3
|
|
|
|
|
|
class TestHasCheckpoints:
|
|
"""Tests for has_checkpoints method"""
|
|
|
|
def test_has_checkpoints_true(self, tmp_path, monkeypatch):
|
|
"""Test has_checkpoints returns True when checkpoints exist"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
cp = checkpoint_dir / "checkpoint_Test.ppz"
|
|
cp.touch()
|
|
|
|
manager = AutosaveManager()
|
|
assert manager.has_checkpoints() is True
|
|
|
|
def test_has_checkpoints_false(self, tmp_path, monkeypatch):
|
|
"""Test has_checkpoints returns False when no checkpoints"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
assert manager.has_checkpoints() is False
|
|
|
|
|
|
class TestGetLatestCheckpoint:
|
|
"""Tests for get_latest_checkpoint method"""
|
|
|
|
def test_get_latest_checkpoint(self, tmp_path, monkeypatch):
|
|
"""Test getting the latest checkpoint"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
# Create checkpoints with different timestamps
|
|
for hour in [8, 10, 12]:
|
|
cp = checkpoint_dir / f"checkpoint_Project_{hour:02d}.ppz"
|
|
cp.touch()
|
|
metadata = {"project_name": "Project", "timestamp": f"2024-01-15T{hour:02d}:00:00"}
|
|
with open(cp.with_suffix(".json"), "w") as f:
|
|
json.dump(metadata, f)
|
|
|
|
manager = AutosaveManager()
|
|
result = manager.get_latest_checkpoint()
|
|
|
|
assert result is not None
|
|
assert "12:00:00" in result[1]["timestamp"]
|
|
|
|
def test_get_latest_checkpoint_none(self, tmp_path, monkeypatch):
|
|
"""Test getting latest checkpoint when none exist"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
manager = AutosaveManager()
|
|
result = manager.get_latest_checkpoint()
|
|
|
|
assert result is None
|
|
|
|
def test_get_latest_checkpoint_filtered(self, tmp_path, monkeypatch):
|
|
"""Test getting latest checkpoint for specific project"""
|
|
checkpoint_dir = tmp_path / "checkpoints"
|
|
checkpoint_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(AutosaveManager, "CHECKPOINT_DIR", checkpoint_dir)
|
|
|
|
# Create checkpoints for different projects
|
|
for name, hour in [("ProjectA", 10), ("ProjectB", 12), ("ProjectA", 8)]:
|
|
cp = checkpoint_dir / f"checkpoint_{name}_{hour:02d}.ppz"
|
|
cp.touch()
|
|
metadata = {"project_name": name, "timestamp": f"2024-01-15T{hour:02d}:00:00"}
|
|
with open(cp.with_suffix(".json"), "w") as f:
|
|
json.dump(metadata, f)
|
|
|
|
manager = AutosaveManager()
|
|
result = manager.get_latest_checkpoint("ProjectA")
|
|
|
|
assert result is not None
|
|
assert result[1]["project_name"] == "ProjectA"
|
|
assert "10:00:00" in result[1]["timestamp"] # Latest for ProjectA
|