Skip to content

Commit 6aab2e6

Browse files
committed
fix: Initial test
1 parent 73697b7 commit 6aab2e6

File tree

3 files changed

+111
-1
lines changed

3 files changed

+111
-1
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "c2pa-python"
7-
version = "0.26.0"
7+
version = "0.27.0"
88
requires-python = ">=3.10"
99
description = "Python bindings for the C2PA Content Authenticity Initiative (CAI) library"
1010
readme = { file = "README.md", content-type = "text/markdown" }

src/c2pa/c2pa.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
'c2pa_builder_set_remote_url',
5050
'c2pa_builder_add_resource',
5151
'c2pa_builder_add_ingredient_from_stream',
52+
'c2pa_builder_add_action',
5253
'c2pa_builder_to_archive',
5354
'c2pa_builder_sign',
5455
'c2pa_manifest_bytes_free',
@@ -403,6 +404,9 @@ def _setup_function(func, argtypes, restype=None):
403404
ctypes.c_char_p,
404405
ctypes.POINTER(C2paStream)],
405406
ctypes.c_int)
407+
_setup_function(_lib.c2pa_builder_add_action,
408+
[ctypes.POINTER(C2paBuilder), ctypes.c_char_p],
409+
ctypes.c_int)
406410
_setup_function(_lib.c2pa_builder_to_archive,
407411
[ctypes.POINTER(C2paBuilder), ctypes.POINTER(C2paStream)],
408412
ctypes.c_int)
@@ -2198,6 +2202,7 @@ class Builder:
21982202
'url_error': "Error setting remote URL: {}",
21992203
'resource_error': "Error adding resource: {}",
22002204
'ingredient_error': "Error adding ingredient: {}",
2205+
'action_error': "Error adding action: {}",
22012206
'archive_error': "Error writing archive: {}",
22022207
'sign_error': "Error during signing: {}",
22032208
'encoding_error': "Invalid UTF-8 characters in manifest: {}",
@@ -2613,6 +2618,38 @@ def add_ingredient_from_file_path(
26132618
except Exception as e:
26142619
raise C2paError.Other(f"Could not add ingredient: {e}") from e
26152620

2621+
def add_action(self, action_json: str) -> None:
2622+
"""Add an action to the builder, that will be placed
2623+
in the actions assertion array in the generated manifest.
2624+
2625+
Args:
2626+
action_json: The JSON action definition
2627+
2628+
Raises:
2629+
C2paError: If there was an error adding the action
2630+
C2paError.Encoding: If the action JSON contains invalid UTF-8 characters
2631+
"""
2632+
self._ensure_valid_state()
2633+
2634+
try:
2635+
action_str = action_json.encode('utf-8')
2636+
except UnicodeError as e:
2637+
raise C2paError.Encoding(
2638+
Builder._ERROR_MESSAGES['encoding_error'].format(str(e))
2639+
)
2640+
2641+
result = _lib.c2pa_builder_add_action(self._builder, action_str)
2642+
2643+
if result != 0:
2644+
error = _parse_operation_result_for_error(_lib.c2pa_error())
2645+
if error:
2646+
raise C2paError(error)
2647+
raise C2paError(
2648+
Builder._ERROR_MESSAGES['action_error'].format(
2649+
"Unknown error"
2650+
)
2651+
)
2652+
26162653
def to_archive(self, stream: Any) -> None:
26172654
"""Write an archive of the builder to a stream.
26182655

tests/test_unit_tests.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,7 @@ def setUp(self):
733733
"actions": [
734734
{
735735
"action": "c2pa.opened"
736+
# Should have more parameters here, but omitted in tests
736737
}
737738
]
738739
}
@@ -2323,6 +2324,78 @@ def test_builder_state_with_invalid_native_pointer(self):
23232324
with self.assertRaises(Error):
23242325
builder.set_no_embed()
23252326

2327+
def test_builder_minimal_manifest_add_actions_and_sign(self):
2328+
# For testing, remove auto-added actions
2329+
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled": false}}}}')
2330+
load_settings('{"builder":{"actions":{"auto_opened_action":{"enabled": false}}}}')
2331+
load_settings('{"builder":{"actions":{"auto_created_action":{"enabled": false}}}}')
2332+
2333+
initial_manifest_definition = {
2334+
"claim_generator": "python_test",
2335+
"claim_generator_info": [{
2336+
"name": "python_test",
2337+
"version": "0.0.1",
2338+
}],
2339+
"format": "image/jpeg",
2340+
"title": "Python Test Image V2",
2341+
}
2342+
builder = Builder.from_json(self.manifestDefinition)
2343+
2344+
builder.add_action('{ "action": "c2pa.created", "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCreation"}')
2345+
2346+
with open(self.testPath2, "rb") as file:
2347+
output = io.BytesIO(bytearray())
2348+
builder.sign(self.signer, "image/jpeg", file, output)
2349+
output.seek(0)
2350+
reader = Reader("image/jpeg", output)
2351+
json_data = reader.json()
2352+
print(json_data)
2353+
manifest_data = json.loads(json_data)
2354+
2355+
# Verify active manifest exists
2356+
self.assertIn("active_manifest", manifest_data)
2357+
active_manifest_id = manifest_data["active_manifest"]
2358+
2359+
# Verify active manifest object exists
2360+
self.assertIn("manifests", manifest_data)
2361+
self.assertIn(active_manifest_id, manifest_data["manifests"])
2362+
active_manifest = manifest_data["manifests"][active_manifest_id]
2363+
2364+
# Verify assertions object exists in active manifest
2365+
self.assertIn("assertions", active_manifest)
2366+
assertions = active_manifest["assertions"]
2367+
2368+
# Find the c2pa.actions.v2 assertion
2369+
actions_assertion = None
2370+
for assertion in assertions:
2371+
if assertion.get("label") == "c2pa.actions.v2":
2372+
actions_assertion = assertion
2373+
break
2374+
2375+
self.assertIsNotNone(actions_assertion)
2376+
2377+
# Check what we added is there
2378+
self.assertIn("data", actions_assertion)
2379+
assertion_data = actions_assertion["data"]
2380+
# Verify the manifest now contains actions
2381+
self.assertIn("actions", assertion_data)
2382+
actions = assertion_data["actions"]
2383+
# Verify "c2pa.created" action exists anywhere in the actions array
2384+
created_action_found = False
2385+
for action in actions:
2386+
if action.get("action") == "c2pa.created":
2387+
created_action_found = True
2388+
break
2389+
2390+
self.assertTrue(created_action_found)
2391+
2392+
builder.close()
2393+
2394+
# Reset settings
2395+
load_settings('{"builder":{"actions":{"auto_placed_action":{"enabled": true}}}}')
2396+
load_settings('{"builder":{"actions":{"auto_opened_action":{"enabled": true}}}}')
2397+
load_settings('{"builder":{"actions":{"auto_created_action":{"enabled": true}}}}')
2398+
23262399

23272400
class TestStream(unittest.TestCase):
23282401
def setUp(self):

0 commit comments

Comments
 (0)