Skip to content

Commit

Permalink
feat: implement binary selfie methods in Python
Browse files Browse the repository at this point in the history
- Implemented to_be_base64 and to_be_file methods
- Added corresponding TODO variants with readonly mode checks
- Added comprehensive tests matching Kotlin implementation
- Follows error handling patterns from Kotlin implementation

Co-Authored-By: [email protected] <[email protected]>
  • Loading branch information
devin-ai-integration[bot] and nedtwigg committed Dec 12, 2024
1 parent 649d9bd commit f8a385b
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 36 deletions.
45 changes: 24 additions & 21 deletions python/example-pytest-selfie/tests/binary_test.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,66 @@
import base64

import pytest
from selfie_lib import expect_selfie
from pytest_selfie.SelfieSettingsAPI import SelfieSettingsAPI
from selfie_lib import Mode, expect_selfie


def test_empty_binary_base64():
"""Test base64 encoding of empty byte array"""
expect_selfie(bytes()).to_be_base64("")


def test_large_binary_base64():
"""Test base64 encoding of large byte array (256 bytes)"""
data = bytes(range(256))
expect_selfie(data).to_be_base64("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==")
expect_selfie(data).to_be_base64(
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="
)


def test_binary_file():
"""Test writing binary data to a file"""
data = b"test binary data"
expect_selfie(data).to_be_file("test_binary.bin")


def test_binary_file_duplicate():
"""Test writing same binary data to a file multiple times"""
data = b"same data"
# First write needs _TODO since it's creating the snapshot
expect_selfie(data).to_be_file_TODO("duplicate.bin")
expect_selfie(data).to_be_file("duplicate.bin")
expect_selfie(data).to_be_file("duplicate.bin")


def test_binary_file_mismatch():
"""Test error handling for mismatched binary data"""
with pytest.raises(AssertionError):
expect_selfie(b"different").to_be_file("test_binary.bin")


def test_binary_file_not_found():
"""Test error handling for non-existent file"""
with pytest.raises(AssertionError) as exc_info:
expect_selfie(b"test").to_be_file("nonexistent.bin")
assert "no such file" in str(exc_info.value)


def test_base64_mismatch():
"""Test error handling for mismatched base64 data"""
data = b"test data"
encoded = base64.b64encode(b"different data").decode()
with pytest.raises(AssertionError):
expect_selfie(data).to_be_base64(encoded)


def test_readonly_mode_todo(monkeypatch):
"""Test error handling in readonly mode for TODO methods"""
from selfie_lib import Mode, _selfieSystem

# Save current mode and create a new readonly system
original_system = _selfieSystem()
readonly_system = original_system.with_mode(Mode.readonly)
monkeypatch.setattr("selfie_lib.SelfieImplementations._selfieSystem", lambda: readonly_system)

try:
with pytest.raises(AssertionError) as exc_info:
expect_selfie(b"test").to_be_file_TODO("test.bin")
assert "readonly mode" in str(exc_info.value)

with pytest.raises(AssertionError) as exc_info:
expect_selfie(b"test").to_be_base64_TODO()
assert "readonly mode" in str(exc_info.value)
finally:
# Restore original system
monkeypatch.setattr("selfie_lib.SelfieImplementations._selfieSystem", lambda: original_system)
monkeypatch.setattr(SelfieSettingsAPI, "calc_mode", lambda self: Mode.readonly)

with pytest.raises(AssertionError) as exc_info:
expect_selfie(b"test").to_be_file_TODO("test.bin")
assert "readonly mode" in str(exc_info.value).lower()

with pytest.raises(AssertionError) as exc_info:
expect_selfie(b"test").to_be_base64_TODO()
assert "readonly mode" in str(exc_info.value).lower()
32 changes: 17 additions & 15 deletions python/selfie-lib/selfie_lib/SelfieImplementations.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ def to_be_base64(self, expected: str) -> bytes:
return actual_bytes

def to_be_base64_TODO(self, _: Any = None) -> bytes:
call = recordCall(False)
if not _selfieSystem().mode.can_write(True, call, _selfieSystem()):
raise _selfieSystem().fs.assert_failed(
f"Can't call `to_be_base64_TODO` in {_selfieSystem().mode} mode!"
)
actual_bytes = self.actual.subject_or_facet(self.only_facet).value_binary()
actual_b64 = base64.b64encode(actual_bytes).decode().replace("\r", "")
_toBeDidntMatch(None, actual_b64, LiteralString())
Expand Down Expand Up @@ -229,23 +234,20 @@ def to_be_file(self, subpath: str) -> bytes:

def to_be_file_TODO(self, subpath: str) -> bytes:
call = recordCall(False)
writable = _selfieSystem().mode.can_write(True, call, _selfieSystem())
actual_bytes = self.actual.subject_or_facet(self.only_facet).value_binary()

if writable:
root_folder = (
_selfieSystem()
.layout.sourcefile_for_call(call.location)
.parent_folder()
)
path = root_folder.resolve_file(subpath)
_selfieSystem().write_to_be_file(path, actual_bytes, call)
_selfieSystem().write_inline(TodoStub.to_be_file.create_literal(), call)
return actual_bytes
else:
if not _selfieSystem().mode.can_write(True, call, _selfieSystem()):
raise _selfieSystem().fs.assert_failed(
f"Can't call `toBeFile_TODO` in {Mode.readonly} mode!"
f"Can't call `to_be_file_TODO` in {_selfieSystem().mode} mode!"
)
actual_bytes = self.actual.subject_or_facet(self.only_facet).value_binary()
root_folder = (
_selfieSystem()
.layout.sourcefile_for_call(call.location)
.parent_folder()
)
path = root_folder.resolve_file(subpath)
_selfieSystem().write_to_be_file(path, actual_bytes, call)
_selfieSystem().write_inline(TodoStub.to_be_file.create_literal(), call)
return actual_bytes


def _checkSrc(value: T) -> T:
Expand Down

0 comments on commit f8a385b

Please sign in to comment.