2
0

test_lfs_integration.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. # test_lfs_integration.py -- Integration tests for LFS with filters
  2. # Copyright (C) 2024 Jelmer Vernooij <jelmer@jelmer.uk>
  3. #
  4. # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  5. # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
  6. # General Public License as published by the Free Software Foundation; version 2.0
  7. # or (at your option) any later version. You can redistribute it and/or
  8. # modify it under the terms of either of these two licenses.
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. # You should have received a copy of the licenses; if not, see
  17. # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
  18. # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
  19. # License, Version 2.0.
  20. #
  21. """Integration tests for LFS with the filter system."""
  22. import shutil
  23. import tempfile
  24. from dulwich.config import ConfigDict
  25. from dulwich.filters import FilterBlobNormalizer, FilterRegistry
  26. from dulwich.lfs import LFSFilterDriver, LFSStore
  27. from dulwich.objects import Blob
  28. from . import TestCase
  29. class LFSFilterIntegrationTests(TestCase):
  30. def setUp(self) -> None:
  31. super().setUp()
  32. # Create temporary directory for LFS store
  33. self.test_dir = tempfile.mkdtemp()
  34. self.addCleanup(shutil.rmtree, self.test_dir)
  35. # Set up LFS store and filter
  36. self.lfs_store = LFSStore.create(self.test_dir)
  37. self.lfs_filter = LFSFilterDriver(self.lfs_store)
  38. # Set up filter registry and normalizer
  39. self.config = ConfigDict()
  40. self.registry = FilterRegistry(self.config)
  41. self.registry.register_driver("lfs", self.lfs_filter)
  42. # Set up gitattributes to use LFS for .bin files
  43. from dulwich.attrs import GitAttributes, Pattern
  44. patterns = [
  45. (Pattern(b"*.bin"), {b"filter": b"lfs"}),
  46. ]
  47. self.gitattributes = GitAttributes(patterns)
  48. self.normalizer = FilterBlobNormalizer(
  49. self.config, self.gitattributes, self.registry
  50. )
  51. def test_lfs_round_trip(self) -> None:
  52. """Test complete LFS round trip through filter normalizer."""
  53. # Create a blob with binary content
  54. original_content = b"This is a large binary file content" * 100
  55. blob = Blob()
  56. blob.data = original_content
  57. # Checkin: should convert to LFS pointer
  58. checked_in = self.normalizer.checkin_normalize(blob, b"large.bin")
  59. # Verify it's an LFS pointer
  60. self.assertLess(len(checked_in.data), len(original_content))
  61. self.assertTrue(
  62. checked_in.data.startswith(b"version https://git-lfs.github.com/spec/v1")
  63. )
  64. # Checkout: should restore original content
  65. checked_out = self.normalizer.checkout_normalize(checked_in, b"large.bin")
  66. # Verify we got back the original content
  67. self.assertEqual(checked_out.data, original_content)
  68. def test_non_lfs_file(self) -> None:
  69. """Test that non-LFS files pass through unchanged."""
  70. # Create a text file (not matching *.bin pattern)
  71. content = b"This is a regular text file"
  72. blob = Blob()
  73. blob.data = content
  74. # Both operations should return the original blob
  75. checked_in = self.normalizer.checkin_normalize(blob, b"file.txt")
  76. self.assertIs(checked_in, blob)
  77. checked_out = self.normalizer.checkout_normalize(blob, b"file.txt")
  78. self.assertIs(checked_out, blob)
  79. def test_lfs_pointer_file(self) -> None:
  80. """Test handling of files that are already LFS pointers."""
  81. # Create an LFS pointer manually
  82. from dulwich.lfs import LFSPointer
  83. # First store some content
  84. content = b"Content to be stored in LFS"
  85. sha = self.lfs_store.write_object([content])
  86. # Create pointer
  87. pointer = LFSPointer(sha, len(content))
  88. blob = Blob()
  89. blob.data = pointer.to_bytes()
  90. # Checkin should recognize it's already a pointer and not change it
  91. checked_in = self.normalizer.checkin_normalize(blob, b"data.bin")
  92. self.assertIs(checked_in, blob)
  93. # Checkout should expand it
  94. checked_out = self.normalizer.checkout_normalize(blob, b"data.bin")
  95. self.assertEqual(checked_out.data, content)
  96. def test_missing_lfs_object(self) -> None:
  97. """Test handling of LFS pointer with missing object."""
  98. from dulwich.lfs import LFSPointer
  99. # Create pointer to non-existent object
  100. pointer = LFSPointer(
  101. "0000000000000000000000000000000000000000000000000000000000000000", 1234
  102. )
  103. blob = Blob()
  104. blob.data = pointer.to_bytes()
  105. # Checkout should return the pointer as-is when object is missing
  106. checked_out = self.normalizer.checkout_normalize(blob, b"missing.bin")
  107. self.assertEqual(checked_out.data, blob.data)