yeriasdk 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. yeriasdk-1.0.0/PKG-INFO +141 -0
  2. yeriasdk-1.0.0/README.md +114 -0
  3. yeriasdk-1.0.0/pyproject.toml +61 -0
  4. yeriasdk-1.0.0/setup.cfg +4 -0
  5. yeriasdk-1.0.0/tests/test_form_view.py +149 -0
  6. yeriasdk-1.0.0/tests/test_intro_fields.py +70 -0
  7. yeriasdk-1.0.0/tests/test_jsonapp.py +107 -0
  8. yeriasdk-1.0.0/tests/test_validators.py +71 -0
  9. yeriasdk-1.0.0/yeriasdk/__init__.py +108 -0
  10. yeriasdk-1.0.0/yeriasdk/core/__init__.py +25 -0
  11. yeriasdk-1.0.0/yeriasdk/core/base_view.py +520 -0
  12. yeriasdk-1.0.0/yeriasdk/core/jsonapp.py +744 -0
  13. yeriasdk-1.0.0/yeriasdk/core/key_store.py +134 -0
  14. yeriasdk-1.0.0/yeriasdk/core/notification.py +29 -0
  15. yeriasdk-1.0.0/yeriasdk/errors/__init__.py +76 -0
  16. yeriasdk-1.0.0/yeriasdk/errors/exceptions.py +493 -0
  17. yeriasdk-1.0.0/yeriasdk/types/__init__.py +128 -0
  18. yeriasdk-1.0.0/yeriasdk/types/models.py +606 -0
  19. yeriasdk-1.0.0/yeriasdk/utils/__init__.py +14 -0
  20. yeriasdk-1.0.0/yeriasdk/utils/file_formats.py +202 -0
  21. yeriasdk-1.0.0/yeriasdk/utils/validators.py +751 -0
  22. yeriasdk-1.0.0/yeriasdk/views/__init__.py +32 -0
  23. yeriasdk-1.0.0/yeriasdk/views/action_grid_view.py +115 -0
  24. yeriasdk-1.0.0/yeriasdk/views/action_list_view.py +88 -0
  25. yeriasdk-1.0.0/yeriasdk/views/base_action_view.py +163 -0
  26. yeriasdk-1.0.0/yeriasdk/views/card_view.py +143 -0
  27. yeriasdk-1.0.0/yeriasdk/views/carousel_view.py +189 -0
  28. yeriasdk-1.0.0/yeriasdk/views/form_view.py +474 -0
  29. yeriasdk-1.0.0/yeriasdk/views/map_view.py +505 -0
  30. yeriasdk-1.0.0/yeriasdk/views/media_view.py +112 -0
  31. yeriasdk-1.0.0/yeriasdk/views/message_view.py +106 -0
  32. yeriasdk-1.0.0/yeriasdk/views/qr_display_view.py +88 -0
  33. yeriasdk-1.0.0/yeriasdk/views/qr_scan_view.py +157 -0
  34. yeriasdk-1.0.0/yeriasdk/views/reader_view.py +270 -0
  35. yeriasdk-1.0.0/yeriasdk/views/timeline_view.py +108 -0
  36. yeriasdk-1.0.0/yeriasdk.egg-info/PKG-INFO +141 -0
  37. yeriasdk-1.0.0/yeriasdk.egg-info/SOURCES.txt +38 -0
  38. yeriasdk-1.0.0/yeriasdk.egg-info/dependency_links.txt +1 -0
  39. yeriasdk-1.0.0/yeriasdk.egg-info/requires.txt +11 -0
  40. yeriasdk-1.0.0/yeriasdk.egg-info/top_level.txt +2 -0
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: yeriasdk
3
+ Version: 1.0.0
4
+ Summary: A modern Python SDK for generating dynamic JSON interfaces
5
+ Author: JsonApp Team
6
+ License: MIT
7
+ Keywords: json,interface,form-builder,sdk,dynamic-ui,mobile-apps
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: cryptography>=41.0.0
18
+ Requires-Dist: markdown>=3.4.0
19
+ Requires-Dist: bleach>=6.0.0
20
+ Requires-Dist: requests>=2.28.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
23
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
24
+ Requires-Dist: black>=23.0.0; extra == "dev"
25
+ Requires-Dist: mypy>=1.5.0; extra == "dev"
26
+ Requires-Dist: ruff>=0.0.280; extra == "dev"
27
+
28
+ # JSONApp Python SDK
29
+
30
+ > **Note**: This is the Python port of the JSONApp SDK. This repository is a monorepo containing multiple language implementations. See the [root README](../README.md) for an overview.
31
+
32
+ A stateless backend library for building views that are sent to renderers (mobile or web). This is the Python port of the TypeScript JSONApp SDK.
33
+
34
+ ## Features
35
+
36
+ - **Stateless Architecture**: No internal state, perfect for serverless and microservices
37
+ - **Ed25519 Signing**: Secure view signing and verification using Ed25519 cryptography
38
+ - **12 View Types**: Form, Reader, ActionList, ActionGrid, QRScan, QRDisplay, Message, Card, Carousel, Timeline, Media, Map
39
+ - **Notifications**: Send signed notifications to users via City-Mate platform
40
+ - **Type Safety**: Full type definitions using Python dataclasses and type hints
41
+ - **Validation**: Built-in field and form validation
42
+ - **Security**: XSS protection, URL validation, input sanitization
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ pip install yeriasdk
48
+ ```
49
+
50
+ ## Quick Start
51
+
52
+ ```python
53
+ from yeriasdk import JsonApp, JsonAppConfig
54
+ from yeriasdk.views import FormView
55
+
56
+ # Initialize JsonApp
57
+ config = JsonAppConfig(
58
+ app_id="my-app",
59
+ view_expiration_minutes=60,
60
+ )
61
+ app = JsonApp(config)
62
+
63
+ # Create a form view
64
+ form = app.create_form_view("registration", "User Registration")
65
+ form.add_text_field("name", "Full Name", is_required=True)
66
+ form.add_email_field("email", "Email", is_required=True)
67
+ form.submit_button("Register")
68
+
69
+ # Serve with signature
70
+ response = app.serve(form)
71
+ print(response.view) # The view JSON
72
+ print(response.signature) # Ed25519 signature
73
+ ```
74
+
75
+ ## View Types
76
+
77
+ ### FormView
78
+ Create forms with various field types (text, email, password, select, file, GPS, etc.)
79
+
80
+ ### ReaderView
81
+ Display rich content with paragraphs, images, markdown, tables, code blocks, etc.
82
+
83
+ ### ActionListView / ActionGridView
84
+ Display lists or grids of actions
85
+
86
+ ### QRScanView / QRDisplayView
87
+ QR code scanning and display
88
+
89
+ ### MessageView
90
+ Display messages with actions
91
+
92
+ ### CardView
93
+ Display card-based content with stats and sections
94
+
95
+ ### CarouselView
96
+ Display carousel slides
97
+
98
+ ### TimelineView
99
+ Display chronological events
100
+
101
+ ### MediaView
102
+ Display audio and video playlists
103
+
104
+ ### MapView
105
+ Display geographic data on maps
106
+
107
+ ## API Parity
108
+
109
+ The Python SDK maintains API parity with the TypeScript version:
110
+
111
+ - Same factory methods: `app.create_form_view()`, `app.create_reader_view()`, etc.
112
+ - Same fluent API: `view.add_field().set_intro().submit_button()` (or `set_note()` for backward compatibility)
113
+ - Same validation and security features
114
+ - Same Ed25519 signing and verification
115
+
116
+ ## Examples
117
+
118
+ See `examples/basic_usage.py` for a complete example.
119
+
120
+ ## Requirements
121
+
122
+ - Python 3.10+
123
+ - cryptography (for Ed25519)
124
+ - markdown (for ReaderView markdown support)
125
+ - bleach (for HTML sanitization)
126
+ - requests (for sending notifications)
127
+
128
+ ## Status
129
+
130
+ ✅ Core classes (BaseView, JsonApp)
131
+ ✅ All 12 view types
132
+ ✅ Type definitions
133
+ ✅ Error handling
134
+ ✅ Validation utilities
135
+ ✅ Ed25519 signing/verification
136
+ ✅ Examples
137
+
138
+
139
+ ## License
140
+
141
+ Ce projet est sous licence MIT. Voir le fichier `LICENSE` pour plus de détails.
@@ -0,0 +1,114 @@
1
+ # JSONApp Python SDK
2
+
3
+ > **Note**: This is the Python port of the JSONApp SDK. This repository is a monorepo containing multiple language implementations. See the [root README](../README.md) for an overview.
4
+
5
+ A stateless backend library for building views that are sent to renderers (mobile or web). This is the Python port of the TypeScript JSONApp SDK.
6
+
7
+ ## Features
8
+
9
+ - **Stateless Architecture**: No internal state, perfect for serverless and microservices
10
+ - **Ed25519 Signing**: Secure view signing and verification using Ed25519 cryptography
11
+ - **12 View Types**: Form, Reader, ActionList, ActionGrid, QRScan, QRDisplay, Message, Card, Carousel, Timeline, Media, Map
12
+ - **Notifications**: Send signed notifications to users via City-Mate platform
13
+ - **Type Safety**: Full type definitions using Python dataclasses and type hints
14
+ - **Validation**: Built-in field and form validation
15
+ - **Security**: XSS protection, URL validation, input sanitization
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install yeriasdk
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```python
26
+ from yeriasdk import JsonApp, JsonAppConfig
27
+ from yeriasdk.views import FormView
28
+
29
+ # Initialize JsonApp
30
+ config = JsonAppConfig(
31
+ app_id="my-app",
32
+ view_expiration_minutes=60,
33
+ )
34
+ app = JsonApp(config)
35
+
36
+ # Create a form view
37
+ form = app.create_form_view("registration", "User Registration")
38
+ form.add_text_field("name", "Full Name", is_required=True)
39
+ form.add_email_field("email", "Email", is_required=True)
40
+ form.submit_button("Register")
41
+
42
+ # Serve with signature
43
+ response = app.serve(form)
44
+ print(response.view) # The view JSON
45
+ print(response.signature) # Ed25519 signature
46
+ ```
47
+
48
+ ## View Types
49
+
50
+ ### FormView
51
+ Create forms with various field types (text, email, password, select, file, GPS, etc.)
52
+
53
+ ### ReaderView
54
+ Display rich content with paragraphs, images, markdown, tables, code blocks, etc.
55
+
56
+ ### ActionListView / ActionGridView
57
+ Display lists or grids of actions
58
+
59
+ ### QRScanView / QRDisplayView
60
+ QR code scanning and display
61
+
62
+ ### MessageView
63
+ Display messages with actions
64
+
65
+ ### CardView
66
+ Display card-based content with stats and sections
67
+
68
+ ### CarouselView
69
+ Display carousel slides
70
+
71
+ ### TimelineView
72
+ Display chronological events
73
+
74
+ ### MediaView
75
+ Display audio and video playlists
76
+
77
+ ### MapView
78
+ Display geographic data on maps
79
+
80
+ ## API Parity
81
+
82
+ The Python SDK maintains API parity with the TypeScript version:
83
+
84
+ - Same factory methods: `app.create_form_view()`, `app.create_reader_view()`, etc.
85
+ - Same fluent API: `view.add_field().set_intro().submit_button()` (or `set_note()` for backward compatibility)
86
+ - Same validation and security features
87
+ - Same Ed25519 signing and verification
88
+
89
+ ## Examples
90
+
91
+ See `examples/basic_usage.py` for a complete example.
92
+
93
+ ## Requirements
94
+
95
+ - Python 3.10+
96
+ - cryptography (for Ed25519)
97
+ - markdown (for ReaderView markdown support)
98
+ - bleach (for HTML sanitization)
99
+ - requests (for sending notifications)
100
+
101
+ ## Status
102
+
103
+ ✅ Core classes (BaseView, JsonApp)
104
+ ✅ All 12 view types
105
+ ✅ Type definitions
106
+ ✅ Error handling
107
+ ✅ Validation utilities
108
+ ✅ Ed25519 signing/verification
109
+ ✅ Examples
110
+
111
+
112
+ ## License
113
+
114
+ Ce projet est sous licence MIT. Voir le fichier `LICENSE` pour plus de détails.
@@ -0,0 +1,61 @@
1
+ [build-system]
2
+ requires = ["setuptools>=65.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "yeriasdk"
7
+ version = "1.0.0"
8
+ description = "A modern Python SDK for generating dynamic JSON interfaces"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ { name = "JsonApp Team" }
14
+ ]
15
+ keywords = ["json", "interface", "form-builder", "sdk", "dynamic-ui", "mobile-apps"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ ]
25
+
26
+ dependencies = [
27
+ "cryptography>=41.0.0",
28
+ "markdown>=3.4.0",
29
+ "bleach>=6.0.0",
30
+ "requests>=2.28.0",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ dev = [
35
+ "pytest>=7.4.0",
36
+ "pytest-cov>=4.1.0",
37
+ "black>=23.0.0",
38
+ "mypy>=1.5.0",
39
+ "ruff>=0.0.280",
40
+ ]
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["."]
44
+ exclude = ["tests*", "examples*"]
45
+
46
+ [tool.black]
47
+ line-length = 100
48
+ target-version = ['py310']
49
+
50
+ [tool.mypy]
51
+ python_version = "3.10"
52
+ warn_return_any = true
53
+ warn_unused_configs = true
54
+ disallow_untyped_defs = false
55
+
56
+ [tool.pytest.ini_options]
57
+ testpaths = ["tests"]
58
+ python_files = ["test_*.py"]
59
+ python_classes = ["Test*"]
60
+ python_functions = ["test_*"]
61
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,149 @@
1
+ """
2
+ Tests for FormView - Critical functionality including separators and intro field
3
+ """
4
+
5
+ import pytest
6
+ from jsonapp import JsonApp, JsonAppConfig
7
+ from jsonapp.views import FormView
8
+ from jsonapp.errors import (
9
+ MissingRequiredParameterError,
10
+ FieldValidationError,
11
+ FieldNotFoundError,
12
+ )
13
+
14
+
15
+ class TestFormView:
16
+ """Test FormView basic functionality"""
17
+
18
+ def test_create_form_view(self):
19
+ """Test creating a basic form view"""
20
+ form = FormView("test-form", "Test Form")
21
+ assert form.id == "test-form"
22
+ assert form.type == "Form"
23
+ assert form.content["title"] == "Test Form"
24
+ assert form.content["intro"] == ""
25
+ assert form.content["fields"] == []
26
+
27
+ def test_set_intro(self):
28
+ """Test setting intro field"""
29
+ form = FormView("test-form", "Test Form")
30
+ form.set_intro("Please fill in the form")
31
+ assert form.content["intro"] == "Please fill in the form"
32
+
33
+ def test_set_note_deprecated(self):
34
+ """Test that set_note() still works for backward compatibility"""
35
+ form = FormView("test-form", "Test Form")
36
+ form.set_note("Old note method")
37
+ assert form.content["intro"] == "Old note method"
38
+
39
+ def test_add_text_field(self):
40
+ """Test adding a text field"""
41
+ form = FormView("test-form", "Test Form")
42
+ form.add_text_field("name", "Full Name", is_required=True)
43
+ assert len(form.content["fields"]) == 1
44
+ field = form.content["fields"][0]
45
+ assert field["fieldType"] == "text"
46
+ assert field["fieldId"] == "name"
47
+ assert field["fieldLabel"] == "Full Name"
48
+ assert field["required"] is True
49
+
50
+ def test_add_separator(self):
51
+ """Test adding a separator field"""
52
+ form = FormView("test-form", "Test Form")
53
+ form.add_text_field("name", "Name")
54
+ form.add_separator()
55
+ form.add_email_field("email", "Email")
56
+
57
+ assert len(form.content["fields"]) == 3
58
+ separator = form.content["fields"][1]
59
+ assert separator["fieldType"] == "separator"
60
+ assert separator["fieldLabel"] == ""
61
+ assert "fieldId" in separator
62
+
63
+ def test_add_separator_with_id(self):
64
+ """Test adding a separator with explicit ID"""
65
+ form = FormView("test-form", "Test Form")
66
+ form.add_separator("custom-separator")
67
+
68
+ separator = form.content["fields"][0]
69
+ assert separator["fieldType"] == "separator"
70
+ assert separator["fieldId"] == "custom-separator"
71
+
72
+ def test_get_field_count(self):
73
+ """Test get_field_count() method"""
74
+ form = FormView("test-form", "Test Form")
75
+ form.add_text_field("name", "Name")
76
+ form.add_separator()
77
+ form.add_email_field("email", "Email")
78
+
79
+ assert form.get_field_count() == 3
80
+ assert form.get_field_count(exclude_separators=True) == 2
81
+
82
+ def test_submit_button(self):
83
+ """Test setting submit button"""
84
+ form = FormView("test-form", "Test Form")
85
+ form.add_text_field("name", "Name")
86
+ form.submit_button("Submit", "POST")
87
+
88
+ assert form.content["submit"] is not None
89
+ assert form.content["submit"]["text"] == "Submit"
90
+ assert form.content["submit"]["method"] == "POST"
91
+
92
+ def test_validation_requires_field(self):
93
+ """Test that form validation requires at least one non-separator field"""
94
+ form = FormView("test-form", "Test Form")
95
+ form.add_separator()
96
+
97
+ result = form._validate()
98
+ assert result.is_valid is False
99
+ assert any("non-separator field" in str(e.message) for e in result.errors)
100
+
101
+ def test_validation_with_real_fields(self):
102
+ """Test that form with real fields passes validation"""
103
+ form = FormView("test-form", "Test Form")
104
+ form.add_text_field("name", "Name")
105
+ form.add_separator()
106
+ form.add_email_field("email", "Email")
107
+
108
+ result = form._validate()
109
+ assert result.is_valid is True
110
+
111
+ def test_get_field(self):
112
+ """Test getting a field by ID"""
113
+ form = FormView("test-form", "Test Form")
114
+ form.add_text_field("name", "Name")
115
+
116
+ field = form.get_field("name")
117
+ assert field is not None
118
+ assert field["fieldId"] == "name"
119
+
120
+ def test_get_field_not_found(self):
121
+ """Test getting a non-existent field"""
122
+ form = FormView("test-form", "Test Form")
123
+ field = form.get_field("nonexistent")
124
+ assert field is None
125
+
126
+ def test_remove_field(self):
127
+ """Test removing a field"""
128
+ form = FormView("test-form", "Test Form")
129
+ form.add_text_field("name", "Name")
130
+ form.add_email_field("email", "Email")
131
+
132
+ result = form.remove_field("name")
133
+ assert result is True
134
+ assert len(form.content["fields"]) == 1
135
+ assert form.content["fields"][0]["fieldId"] == "email"
136
+
137
+ def test_inject_data(self):
138
+ """Test injecting data into form fields"""
139
+ form = FormView("test-form", "Test Form")
140
+ form.add_text_field("name", "Name")
141
+ form.add_email_field("email", "Email")
142
+
143
+ errors = form.inject_data({"name": "John Doe", "email": "john@example.com"})
144
+ assert len(errors) == 0
145
+
146
+ name_field = form.get_field("name")
147
+ assert name_field["value"] == "John Doe"
148
+
149
+
@@ -0,0 +1,70 @@
1
+ """
2
+ Tests for intro field support across all views
3
+ """
4
+
5
+ import pytest
6
+ from jsonapp import JsonApp, JsonAppConfig
7
+ from jsonapp.views import (
8
+ ActionListView,
9
+ ActionGridView,
10
+ MapView,
11
+ ReaderView,
12
+ QRScanView,
13
+ MessageView,
14
+ TimelineView,
15
+ MediaView,
16
+ )
17
+
18
+
19
+ class TestIntroFields:
20
+ """Test intro field support across views"""
21
+
22
+ def test_action_list_view_intro(self):
23
+ """Test ActionListView intro support"""
24
+ view = ActionListView("test-list", "Test List")
25
+ view.set_intro("Select an option")
26
+ assert view.content["intro"] == "Select an option"
27
+
28
+ def test_action_grid_view_intro(self):
29
+ """Test ActionGridView intro support"""
30
+ view = ActionGridView("test-grid", "Test Grid")
31
+ view.set_intro("Choose an action")
32
+ assert view.content["intro"] == "Choose an action"
33
+
34
+ def test_map_view_intro(self):
35
+ """Test MapView intro support"""
36
+ view = MapView("test-map", "Test Map")
37
+ view.set_intro("Find locations on the map")
38
+ assert view.content["intro"] == "Find locations on the map"
39
+
40
+ def test_reader_view_intro(self):
41
+ """Test ReaderView intro support"""
42
+ view = ReaderView("test-reader", "Test Reader")
43
+ view.set_intro("Read the content below")
44
+ assert view.content["intro"] == "Read the content below"
45
+
46
+ def test_qr_scan_view_intro(self):
47
+ """Test QRScanView intro support"""
48
+ view = QRScanView("test-scan", "Test Scanner")
49
+ view.set_intro("Point camera at QR code")
50
+ assert view.content["intro"] == "Point camera at QR code"
51
+
52
+ def test_message_view_intro(self):
53
+ """Test MessageView intro support"""
54
+ view = MessageView("test-msg", "Test Message")
55
+ view.set_intro("Welcome message")
56
+ assert view.content["intro"] == "Welcome message"
57
+
58
+ def test_timeline_view_intro(self):
59
+ """Test TimelineView intro support"""
60
+ view = TimelineView("test-timeline", "Test Timeline")
61
+ view.set_intro("View your timeline")
62
+ assert view.content["intro"] == "View your timeline"
63
+
64
+ def test_media_view_intro(self):
65
+ """Test MediaView intro support"""
66
+ view = MediaView("test-media", "Test Media")
67
+ view.set_intro("Browse media")
68
+ assert view.content["intro"] == "Browse media"
69
+
70
+
@@ -0,0 +1,107 @@
1
+ """
2
+ Tests for JsonApp - Signing, verification, and factory methods
3
+ """
4
+
5
+ import pytest
6
+ import time
7
+ from jsonapp import JsonApp, JsonAppConfig
8
+ from jsonapp.views import FormView
9
+ from jsonapp.errors import (
10
+ AppIdMismatchError,
11
+ ViewExpiredError,
12
+ SignatureVerificationError,
13
+ )
14
+
15
+
16
+ class TestJsonApp:
17
+ """Test JsonApp core functionality"""
18
+
19
+ def test_create_jsonapp(self):
20
+ """Test creating JsonApp instance"""
21
+ config = JsonAppConfig(app_id="test-app", view_expiration_minutes=60)
22
+ app = JsonApp(config)
23
+ assert app.config.app_id == "test-app"
24
+ assert app.config.view_expiration_minutes == 60
25
+
26
+ def test_create_form_view(self):
27
+ """Test factory method for FormView"""
28
+ config = JsonAppConfig(app_id="test-app")
29
+ app = JsonApp(config)
30
+ form = app.create_form_view("test-form", "Test Form")
31
+ assert isinstance(form, FormView)
32
+ assert form.id == "test-form"
33
+
34
+ def test_serve_view(self):
35
+ """Test serving a view with signature"""
36
+ config = JsonAppConfig(app_id="test-app")
37
+ app = JsonApp(config)
38
+ form = app.create_form_view("test-form", "Test Form")
39
+ form.add_text_field("name", "Name")
40
+
41
+ response = app.serve(form)
42
+ assert response.app_id == "test-app"
43
+ assert response.signature is not None
44
+ assert response.timestamp > 0
45
+ assert response.view is not None
46
+ assert response.view["id"] == "test-form"
47
+
48
+ def test_get_public_key(self):
49
+ """Test getting public key"""
50
+ config = JsonAppConfig(app_id="test-app")
51
+ app = JsonApp(config)
52
+ public_key = app.get_public_key()
53
+ assert public_key is not None
54
+ assert "BEGIN PUBLIC KEY" in public_key or "BEGIN PUBLIC KEY" in public_key
55
+
56
+ def test_verify_integrity_success(self):
57
+ """Test verifying integrity of a valid response"""
58
+ config = JsonAppConfig(app_id="test-app", view_expiration_minutes=60)
59
+ app = JsonApp(config)
60
+ form = app.create_form_view("test-form", "Test Form")
61
+ form.add_text_field("name", "Name")
62
+
63
+ response = app.serve(form)
64
+ result = app.verify_integrity(response)
65
+ assert result is True
66
+
67
+ def test_verify_integrity_app_id_mismatch(self):
68
+ """Test verification fails with wrong app ID"""
69
+ config1 = JsonAppConfig(app_id="app-1")
70
+ app1 = JsonApp(config1)
71
+ form = app1.create_form_view("test-form", "Test Form")
72
+ form.add_text_field("name", "Name")
73
+ response = app1.serve(form)
74
+
75
+ config2 = JsonAppConfig(app_id="app-2")
76
+ app2 = JsonApp(config2)
77
+
78
+ with pytest.raises(AppIdMismatchError):
79
+ app2.verify_integrity(response)
80
+
81
+ def test_static_sign_view(self):
82
+ """Test static sign_view method"""
83
+ config = JsonAppConfig(app_id="test-app")
84
+ app = JsonApp(config)
85
+ form = app.create_form_view("test-form", "Test Form")
86
+ form.add_text_field("name", "Name")
87
+ view_json = form.to_json()
88
+
89
+ private_key = app.get_public_key() # In real usage, use private key
90
+ # Note: This test would need actual private key to work properly
91
+ # For now, just verify the method exists and accepts parameters
92
+ assert hasattr(JsonApp, "sign_view")
93
+
94
+ def test_static_verify_signature(self):
95
+ """Test static verify_signature method"""
96
+ config = JsonAppConfig(app_id="test-app")
97
+ app = JsonApp(config)
98
+ form = app.create_form_view("test-form", "Test Form")
99
+ form.add_text_field("name", "Name")
100
+
101
+ response = app.serve(form)
102
+ public_key = app.get_public_key()
103
+
104
+ result = JsonApp.verify_signature(public_key, response)
105
+ assert result is True
106
+
107
+