v2kit 0.5__tar.gz → 0.6__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 (38) hide show
  1. {v2kit-0.5 → v2kit-0.6}/CHANGELOG.md +15 -1
  2. {v2kit-0.5 → v2kit-0.6}/PKG-INFO +20 -6
  3. {v2kit-0.5 → v2kit-0.6}/README.md +2 -2
  4. {v2kit-0.5 → v2kit-0.6}/SECURITY.md +2 -2
  5. {v2kit-0.5 → v2kit-0.6}/setup.py +3 -3
  6. {v2kit-0.5 → v2kit-0.6}/tests/test_errors.py +30 -3
  7. {v2kit-0.5 → v2kit-0.6}/tests/test_functions.py +9 -1
  8. {v2kit-0.5 → v2kit-0.6}/tests/test_http.py +60 -1
  9. {v2kit-0.5 → v2kit-0.6}/tests/test_shadowsocks.py +72 -1
  10. {v2kit-0.5 → v2kit-0.6}/tests/test_socks.py +64 -1
  11. {v2kit-0.5 → v2kit-0.6}/tests/test_trojan.py +74 -1
  12. {v2kit-0.5 → v2kit-0.6}/tests/test_vless.py +68 -1
  13. {v2kit-0.5 → v2kit-0.6}/tests/test_vmess.py +103 -1
  14. {v2kit-0.5 → v2kit-0.6}/v2kit/functions.py +4 -12
  15. {v2kit-0.5 → v2kit-0.6}/v2kit/models/base.py +49 -14
  16. {v2kit-0.5 → v2kit-0.6}/v2kit/models/http.py +2 -8
  17. {v2kit-0.5 → v2kit-0.6}/v2kit/models/shadowsocks.py +6 -23
  18. {v2kit-0.5 → v2kit-0.6}/v2kit/models/socks.py +4 -16
  19. {v2kit-0.5 → v2kit-0.6}/v2kit/models/trojan.py +3 -12
  20. {v2kit-0.5 → v2kit-0.6}/v2kit/models/vless.py +3 -12
  21. {v2kit-0.5 → v2kit-0.6}/v2kit/models/vmess.py +7 -30
  22. {v2kit-0.5 → v2kit-0.6}/v2kit/params.py +1 -1
  23. {v2kit-0.5 → v2kit-0.6}/v2kit/parsers.py +30 -12
  24. {v2kit-0.5 → v2kit-0.6}/v2kit/utils.py +4 -7
  25. {v2kit-0.5 → v2kit-0.6}/v2kit/validators.py +4 -10
  26. {v2kit-0.5 → v2kit-0.6}/v2kit.egg-info/PKG-INFO +20 -6
  27. {v2kit-0.5 → v2kit-0.6}/AUTHORS.md +0 -0
  28. {v2kit-0.5 → v2kit-0.6}/LICENSE +0 -0
  29. {v2kit-0.5 → v2kit-0.6}/MANIFEST.in +0 -0
  30. {v2kit-0.5 → v2kit-0.6}/requirements-dev.txt +0 -0
  31. {v2kit-0.5 → v2kit-0.6}/setup.cfg +0 -0
  32. {v2kit-0.5 → v2kit-0.6}/v2kit/__init__.py +0 -0
  33. {v2kit-0.5 → v2kit-0.6}/v2kit/__main__.py +0 -0
  34. {v2kit-0.5 → v2kit-0.6}/v2kit/errors.py +0 -0
  35. {v2kit-0.5 → v2kit-0.6}/v2kit/models/__init__.py +0 -0
  36. {v2kit-0.5 → v2kit-0.6}/v2kit.egg-info/SOURCES.txt +0 -0
  37. {v2kit-0.5 → v2kit-0.6}/v2kit.egg-info/dependency_links.txt +0 -0
  38. {v2kit-0.5 → v2kit-0.6}/v2kit.egg-info/top_level.txt +0 -0
@@ -5,6 +5,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
+ ## [0.6] - 2026-09-01
9
+ ### Added
10
+ - `clear_extra` method
11
+ - `set_extra_item` method
12
+ - `get_extra_item` method
13
+ - `remove_extra_item` method
14
+ ### Changed
15
+ - `_get_protocol` function modified
16
+ - `decode_subscription` function blank line bug fixed
17
+ - Port validation bug fixed
18
+ - AlterId validation bug fixed
19
+ - Test system modified
20
+ - Parsers updated
8
21
  ## [0.5] - 2026-07-31
9
22
  ### Added
10
23
  - `encoded_label` property
@@ -65,7 +78,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
65
78
  - `encode_subscription` function
66
79
  - `decode_subscription` function
67
80
 
68
- [Unreleased]: https://github.com/sepandhaghighi/v2kit/compare/v0.5...dev
81
+ [Unreleased]: https://github.com/sepandhaghighi/v2kit/compare/v0.6...dev
82
+ [0.6]: https://github.com/sepandhaghighi/v2kit/compare/v0.5...v0.6
69
83
  [0.5]: https://github.com/sepandhaghighi/v2kit/compare/v0.4...v0.5
70
84
  [0.4]: https://github.com/sepandhaghighi/v2kit/compare/v0.3...v0.4
71
85
  [0.3]: https://github.com/sepandhaghighi/v2kit/compare/v0.2...v0.3
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: v2kit
3
- Version: 0.5
3
+ Version: 0.6
4
4
  Summary: V2Kit: A Lightweight Toolkit for V2Ray Config Manipulation
5
5
  Home-page: https://github.com/sepandhaghighi/v2kit
6
- Download-URL: https://github.com/sepandhaghighi/v2kit/tarball/v0.5
6
+ Download-URL: https://github.com/sepandhaghighi/v2kit/tarball/v0.6
7
7
  Author: Sepand Haghighi
8
8
  Author-email: me@sepand.tech
9
9
  License: MIT
10
10
  Project-URL: Source, https://github.com/sepandhaghighi/v2kit
11
11
  Keywords: v2ray v2ray-config v2ray-tools vmess vless trojan shadowsocks proxy subscription network
12
- Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Natural Language :: English
14
14
  Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Operating System :: OS Independent
@@ -96,13 +96,13 @@ V2Kit is a lightweight and extensible Python toolkit for working with V2Ray prox
96
96
  ## Installation
97
97
 
98
98
  ### Source Code
99
- - Download [Version 0.5](https://github.com/sepandhaghighi/v2kit/archive/v0.5.zip) or [Latest Source](https://github.com/sepandhaghighi/v2kit/archive/dev.zip)
99
+ - Download [Version 0.6](https://github.com/sepandhaghighi/v2kit/archive/v0.6.zip) or [Latest Source](https://github.com/sepandhaghighi/v2kit/archive/dev.zip)
100
100
  - `pip install .`
101
101
 
102
102
  ### PyPI
103
103
 
104
104
  - Check [Python Packaging User Guide](https://packaging.python.org/installing/)
105
- - `pip install v2kit==0.5`
105
+ - `pip install v2kit==0.6`
106
106
 
107
107
  ## Supported Protocols
108
108
 
@@ -211,6 +211,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
211
211
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
212
212
 
213
213
  ## [Unreleased]
214
+ ## [0.6] - 2026-09-01
215
+ ### Added
216
+ - `clear_extra` method
217
+ - `set_extra_item` method
218
+ - `get_extra_item` method
219
+ - `remove_extra_item` method
220
+ ### Changed
221
+ - `_get_protocol` function modified
222
+ - `decode_subscription` function blank line bug fixed
223
+ - Port validation bug fixed
224
+ - AlterId validation bug fixed
225
+ - Test system modified
226
+ - Parsers updated
214
227
  ## [0.5] - 2026-07-31
215
228
  ### Added
216
229
  - `encoded_label` property
@@ -271,7 +284,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
271
284
  - `encode_subscription` function
272
285
  - `decode_subscription` function
273
286
 
274
- [Unreleased]: https://github.com/sepandhaghighi/v2kit/compare/v0.5...dev
287
+ [Unreleased]: https://github.com/sepandhaghighi/v2kit/compare/v0.6...dev
288
+ [0.6]: https://github.com/sepandhaghighi/v2kit/compare/v0.5...v0.6
275
289
  [0.5]: https://github.com/sepandhaghighi/v2kit/compare/v0.4...v0.5
276
290
  [0.4]: https://github.com/sepandhaghighi/v2kit/compare/v0.3...v0.4
277
291
  [0.3]: https://github.com/sepandhaghighi/v2kit/compare/v0.2...v0.3
@@ -51,13 +51,13 @@ V2Kit is a lightweight and extensible Python toolkit for working with V2Ray prox
51
51
  ## Installation
52
52
 
53
53
  ### Source Code
54
- - Download [Version 0.5](https://github.com/sepandhaghighi/v2kit/archive/v0.5.zip) or [Latest Source](https://github.com/sepandhaghighi/v2kit/archive/dev.zip)
54
+ - Download [Version 0.6](https://github.com/sepandhaghighi/v2kit/archive/v0.6.zip) or [Latest Source](https://github.com/sepandhaghighi/v2kit/archive/dev.zip)
55
55
  - `pip install .`
56
56
 
57
57
  ### PyPI
58
58
 
59
59
  - Check [Python Packaging User Guide](https://packaging.python.org/installing/)
60
- - `pip install v2kit==0.5`
60
+ - `pip install v2kit==0.6`
61
61
 
62
62
  ## Supported Protocols
63
63
 
@@ -4,8 +4,8 @@
4
4
 
5
5
  | Version | Supported |
6
6
  | ------------- | ------------------ |
7
- | 0.5 | :white_check_mark: |
8
- | < 0.5 | :x: |
7
+ | 0.6 | :white_check_mark: |
8
+ | < 0.6 | :x: |
9
9
 
10
10
  ## Reporting a Vulnerability
11
11
 
@@ -23,7 +23,7 @@ def read_description() -> str:
23
23
  setup(
24
24
  name='v2kit',
25
25
  packages=['v2kit', 'v2kit.models'],
26
- version='0.5',
26
+ version='0.6',
27
27
  description='V2Kit: A Lightweight Toolkit for V2Ray Config Manipulation',
28
28
  long_description=read_description(),
29
29
  long_description_content_type='text/markdown',
@@ -31,14 +31,14 @@ setup(
31
31
  author='Sepand Haghighi',
32
32
  author_email='me@sepand.tech',
33
33
  url='https://github.com/sepandhaghighi/v2kit',
34
- download_url='https://github.com/sepandhaghighi/v2kit/tarball/v0.5',
34
+ download_url='https://github.com/sepandhaghighi/v2kit/tarball/v0.6',
35
35
  keywords='v2ray v2ray-config v2ray-tools vmess vless trojan shadowsocks proxy subscription network',
36
36
  project_urls={
37
37
  'Source': 'https://github.com/sepandhaghighi/v2kit'},
38
38
  install_requires=[],
39
39
  python_requires='>=3.8',
40
40
  classifiers=[
41
- 'Development Status :: 3 - Alpha',
41
+ 'Development Status :: 4 - Beta',
42
42
  'Natural Language :: English',
43
43
  'License :: OSI Approved :: MIT License',
44
44
  'Operating System :: OS Independent',
@@ -143,7 +143,7 @@ def test_vless_invalid_uuid():
143
143
  )
144
144
 
145
145
 
146
- def test_vless_invalid_port():
146
+ def test_vless_invalid_port1():
147
147
  with pytest.raises(V2kitValidationError):
148
148
  VLESSConfig(
149
149
  uuid=VALID_UUID,
@@ -152,6 +152,15 @@ def test_vless_invalid_port():
152
152
  )
153
153
 
154
154
 
155
+ def test_vless_invalid_port2():
156
+ with pytest.raises(V2kitValidationError):
157
+ VLESSConfig(
158
+ uuid=VALID_UUID,
159
+ address="example.com",
160
+ port=True,
161
+ )
162
+
163
+
155
164
  def test_vless_empty_address():
156
165
  with pytest.raises(V2kitValidationError):
157
166
  VLESSConfig(
@@ -161,7 +170,7 @@ def test_vless_empty_address():
161
170
  )
162
171
 
163
172
 
164
- def test_vmess_invalid_alter_id():
173
+ def test_vmess_invalid_alter_id1():
165
174
  with pytest.raises(V2kitValidationError):
166
175
  VMESSConfig(
167
176
  uuid=VALID_UUID,
@@ -171,6 +180,16 @@ def test_vmess_invalid_alter_id():
171
180
  )
172
181
 
173
182
 
183
+ def test_vmess_invalid_alter_id2():
184
+ with pytest.raises(V2kitValidationError):
185
+ VMESSConfig(
186
+ uuid=VALID_UUID,
187
+ address="example.com",
188
+ port=443,
189
+ alter_id=True,
190
+ )
191
+
192
+
174
193
  def test_trojan_empty_password():
175
194
  with pytest.raises(V2kitValidationError):
176
195
  TrojanConfig(
@@ -190,7 +209,7 @@ def test_shadowsocks_empty_encryption():
190
209
  )
191
210
 
192
211
 
193
- def test_socks_invalid_port():
212
+ def test_socks_invalid_port1():
194
213
  with pytest.raises(V2kitValidationError):
195
214
  SocksConfig(
196
215
  address="example.com",
@@ -198,6 +217,14 @@ def test_socks_invalid_port():
198
217
  )
199
218
 
200
219
 
220
+ def test_socks_invalid_port2():
221
+ with pytest.raises(V2kitValidationError):
222
+ SocksConfig(
223
+ address="example.com",
224
+ port=False,
225
+ )
226
+
227
+
201
228
  def test_socks_empty_address():
202
229
  with pytest.raises(V2kitValidationError):
203
230
  SocksConfig(
@@ -135,7 +135,7 @@ def test_encode_subscription():
135
135
  assert len(result) > 0
136
136
 
137
137
 
138
- def test_decode_subscription():
138
+ def test_decode_subscription1():
139
139
  configs = [
140
140
  VMESS_CONFIG,
141
141
  VLESS_CONFIG,
@@ -148,6 +148,14 @@ def test_decode_subscription():
148
148
  assert result == configs
149
149
 
150
150
 
151
+ def test_decode_subscription2():
152
+ subscription = "dmxlc3M6Ly8xYzRiNGJjYS1lM2ZmLTRjYTgtYTA2Mi02ZjM5OWFkM2NmNDVAZXhhbXBsZS5jb206NDQzP3NlY3VyaXR5PXRscyN2bGVzcy10ZXN0Cgp0cm9qYW46Ly9wYXNzd29yZEBleGFtcGxlLmNvbTo0NDM/c2VjdXJpdHk9dGxzI3Ryb2phbi10ZXN0CgpzczovL1lXVnpMVEkxTmkxblkyMDZjR0Z6YzNkdmNtUT1AZXhhbXBsZS5jb206ODM4OCNzcy10ZXN0Cg=="
153
+
154
+ result = decode_subscription(subscription)
155
+
156
+ assert len(result) == 3
157
+
158
+
151
159
  def test_encode_decode_subscription_roundtrip():
152
160
  configs = [
153
161
  VMESS_CONFIG,
@@ -79,6 +79,59 @@ def test_update_extra():
79
79
  assert config.extra["foo"] == "bar"
80
80
 
81
81
 
82
+ def test_clear_extra():
83
+ config = HttpConfig(
84
+ address="example.com",
85
+ port=1080,
86
+ extra={"foo": "bar"},
87
+ )
88
+
89
+ config.clear_extra()
90
+
91
+ assert config.extra == {}
92
+
93
+
94
+ def test_set_extra_item():
95
+ config = HttpConfig(
96
+ address="example.com",
97
+ port=1080,
98
+ )
99
+
100
+ config.set_extra_item(
101
+ "foo",
102
+ "bar",
103
+ )
104
+
105
+ assert config.extra["foo"] == "bar"
106
+
107
+
108
+ def test_get_extra_item():
109
+ config = HttpConfig(
110
+ address="example.com",
111
+ port=1080,
112
+ extra={"foo": "bar"},
113
+ )
114
+
115
+ assert config.get_extra_item("foo") == "bar"
116
+ assert config.get_extra_item("missing") is None
117
+ assert config.get_extra_item(
118
+ "missing",
119
+ "default",
120
+ ) == "default"
121
+
122
+
123
+ def test_remove_extra_item():
124
+ config = HttpConfig(
125
+ address="example.com",
126
+ port=1080,
127
+ extra={"foo": "bar"},
128
+ )
129
+
130
+ config.remove_extra_item("foo")
131
+
132
+ assert config.extra == {}
133
+
134
+
82
135
  def test_method_chaining():
83
136
  config = HttpConfig(
84
137
  address="example.com",
@@ -93,12 +146,18 @@ def test_method_chaining():
93
146
  "user"
94
147
  ).update_password(
95
148
  "password"
96
- )
149
+ ).set_extra_item(
150
+ "foo",
151
+ "bar",
152
+ ).remove_extra_item(
153
+ "foo"
154
+ ).clear_extra()
97
155
 
98
156
  assert config.address == "example.org"
99
157
  assert config.port == 8080
100
158
  assert config.username == "user"
101
159
  assert config.password == "password"
160
+ assert config.extra == {}
102
161
 
103
162
 
104
163
  @pytest.mark.parametrize(
@@ -67,12 +67,18 @@ def test_method_chaining():
67
67
  "example.org"
68
68
  ).update_port(
69
69
  443
70
- )
70
+ ).set_extra_item(
71
+ "plugin",
72
+ "v2ray-plugin",
73
+ ).remove_extra_item(
74
+ "plugin"
75
+ ).clear_extra()
71
76
 
72
77
  assert config.encryption == "chacha20-ietf-poly1305"
73
78
  assert config.password == "secret"
74
79
  assert config.address == "example.org"
75
80
  assert config.port == 443
81
+ assert config.extra == {}
76
82
 
77
83
 
78
84
  @pytest.mark.parametrize(
@@ -184,6 +190,71 @@ def test_update_extra():
184
190
  assert config.extra["plugin"] == "v2ray-plugin"
185
191
 
186
192
 
193
+ def test_clear_extra():
194
+ config = ShadowsocksConfig(
195
+ encryption="aes-256-gcm",
196
+ password="password",
197
+ address="example.com",
198
+ port=8388,
199
+ extra={"plugin": "v2ray-plugin"},
200
+ )
201
+
202
+ config.clear_extra()
203
+
204
+ assert config.extra == {}
205
+
206
+
207
+ def test_set_extra_item():
208
+ config = ShadowsocksConfig(
209
+ encryption="aes-256-gcm",
210
+ password="password",
211
+ address="example.com",
212
+ port=8388,
213
+ )
214
+
215
+ config.set_extra_item(
216
+ "plugin",
217
+ "v2ray-plugin",
218
+ )
219
+
220
+ assert config.extra["plugin"] == "v2ray-plugin"
221
+
222
+
223
+ def test_get_extra_item():
224
+ config = ShadowsocksConfig(
225
+ encryption="aes-256-gcm",
226
+ password="password",
227
+ address="example.com",
228
+ port=8388,
229
+ extra={"plugin": "v2ray-plugin"},
230
+ )
231
+
232
+ assert config.get_extra_item(
233
+ "plugin"
234
+ ) == "v2ray-plugin"
235
+ assert config.get_extra_item("missing") is None
236
+ assert config.get_extra_item(
237
+ "missing",
238
+ "default",
239
+ ) == "default"
240
+
241
+
242
+ def test_remove_extra_item():
243
+ config = ShadowsocksConfig(
244
+ encryption="aes-256-gcm",
245
+ password="password",
246
+ address="example.com",
247
+ port=8388,
248
+ extra={"plugin": "v2ray-plugin"},
249
+ )
250
+
251
+ config.remove_extra_item(
252
+ "plugin"
253
+ )
254
+
255
+ assert config.extra == {}
256
+
257
+
187
258
  def test_update_methods():
188
259
  config = ShadowsocksConfig(
189
260
  encryption="aes-256-gcm",
@@ -61,12 +61,18 @@ def test_method_chaining():
61
61
  "user"
62
62
  ).update_password(
63
63
  "password"
64
- )
64
+ ).set_extra_item(
65
+ "version",
66
+ "5",
67
+ ).remove_extra_item(
68
+ "version"
69
+ ).clear_extra()
65
70
 
66
71
  assert config.address == "example.org"
67
72
  assert config.port == 2080
68
73
  assert config.username == "user"
69
74
  assert config.password == "password"
75
+ assert config.extra == {}
70
76
 
71
77
 
72
78
  @pytest.mark.parametrize(
@@ -121,6 +127,63 @@ def test_update_extra():
121
127
  assert config.extra["version"] == "5"
122
128
 
123
129
 
130
+ def test_clear_extra():
131
+ config = SocksConfig(
132
+ address="example.com",
133
+ port=1080,
134
+ extra={"version": "5"},
135
+ )
136
+
137
+ config.clear_extra()
138
+
139
+ assert config.extra == {}
140
+
141
+
142
+ def test_set_extra_item():
143
+ config = SocksConfig(
144
+ address="example.com",
145
+ port=1080,
146
+ )
147
+
148
+ config.set_extra_item(
149
+ "version",
150
+ "5",
151
+ )
152
+
153
+ assert config.extra["version"] == "5"
154
+
155
+
156
+ def test_get_extra_item():
157
+ config = SocksConfig(
158
+ address="example.com",
159
+ port=1080,
160
+ extra={"version": "5"},
161
+ )
162
+
163
+ assert config.get_extra_item(
164
+ "version"
165
+ ) == "5"
166
+ assert config.get_extra_item("missing") is None
167
+ assert config.get_extra_item(
168
+ "missing",
169
+ "default",
170
+ ) == "default"
171
+
172
+
173
+ def test_remove_extra_item():
174
+ config = SocksConfig(
175
+ address="example.com",
176
+ port=1080,
177
+ extra={"version": "5"},
178
+ )
179
+
180
+ config.remove_extra_item(
181
+ "version"
182
+ )
183
+
184
+ assert config.extra == {}
185
+
186
+
124
187
  def test_to_uri_roundtrip():
125
188
  config = SocksConfig(
126
189
  address="example.com",
@@ -55,11 +55,23 @@ def test_method_chaining():
55
55
  port=443,
56
56
  )
57
57
 
58
- config.update_password("new-password").update_address("example.org").update_port(8443)
58
+ config.update_password(
59
+ "new-password"
60
+ ).update_address(
61
+ "example.org"
62
+ ).update_port(
63
+ 8443
64
+ ).set_extra_item(
65
+ "security",
66
+ "tls",
67
+ ).remove_extra_item(
68
+ "security"
69
+ ).clear_extra()
59
70
 
60
71
  assert config.password == "new-password"
61
72
  assert config.address == "example.org"
62
73
  assert config.port == 8443
74
+ assert config.extra == {}
63
75
 
64
76
 
65
77
  @pytest.mark.parametrize(
@@ -159,6 +171,67 @@ def test_update_extra():
159
171
  assert config.extra["security"] == "tls"
160
172
 
161
173
 
174
+ def test_clear_extra():
175
+ config = TrojanConfig(
176
+ password="password",
177
+ address="example.com",
178
+ port=443,
179
+ extra={"security": "tls"},
180
+ )
181
+
182
+ config.clear_extra()
183
+
184
+ assert config.extra == {}
185
+
186
+
187
+ def test_set_extra_item():
188
+ config = TrojanConfig(
189
+ password="password",
190
+ address="example.com",
191
+ port=443,
192
+ )
193
+
194
+ config.set_extra_item(
195
+ "security",
196
+ "tls",
197
+ )
198
+
199
+ assert config.extra["security"] == "tls"
200
+
201
+
202
+ def test_get_extra_item():
203
+ config = TrojanConfig(
204
+ password="password",
205
+ address="example.com",
206
+ port=443,
207
+ extra={"security": "tls"},
208
+ )
209
+
210
+ assert config.get_extra_item(
211
+ "security"
212
+ ) == "tls"
213
+ assert config.get_extra_item("missing") is None
214
+ assert config.get_extra_item(
215
+ "missing",
216
+ "default",
217
+ ) == "default"
218
+
219
+
220
+ def test_remove_extra_item():
221
+ config = TrojanConfig(
222
+ password="password",
223
+ address="example.com",
224
+ port=443,
225
+ extra={"security": "tls"},
226
+ )
227
+
228
+ config.remove_extra_item(
229
+ "security"
230
+ )
231
+
232
+ assert config.extra == {}
233
+
234
+
162
235
  def test_update_methods():
163
236
  config = TrojanConfig(
164
237
  password="password",
@@ -61,11 +61,17 @@ def test_method_chaining():
61
61
  "example.org"
62
62
  ).update_port(
63
63
  8443
64
- )
64
+ ).set_extra_item(
65
+ "security",
66
+ "tls",
67
+ ).remove_extra_item(
68
+ "security"
69
+ ).clear_extra()
65
70
 
66
71
  assert config.uuid == "2c4b4bca-e3ff-4ca8-a062-6f399ad3cf45"
67
72
  assert config.address == "example.org"
68
73
  assert config.port == 8443
74
+ assert config.extra == {}
69
75
 
70
76
 
71
77
  @pytest.mark.parametrize(
@@ -168,6 +174,67 @@ def test_update_extra():
168
174
  assert config.extra["security"] == "tls"
169
175
 
170
176
 
177
+ def test_clear_extra():
178
+ config = VLESSConfig(
179
+ uuid="1c4b4bca-e3ff-4ca8-a062-6f399ad3cf45",
180
+ address="example.com",
181
+ port=443,
182
+ extra={"security": "tls"},
183
+ )
184
+
185
+ config.clear_extra()
186
+
187
+ assert config.extra == {}
188
+
189
+
190
+ def test_set_extra_item():
191
+ config = VLESSConfig(
192
+ uuid="1c4b4bca-e3ff-4ca8-a062-6f399ad3cf45",
193
+ address="example.com",
194
+ port=443,
195
+ )
196
+
197
+ config.set_extra_item(
198
+ "security",
199
+ "tls",
200
+ )
201
+
202
+ assert config.extra["security"] == "tls"
203
+
204
+
205
+ def test_get_extra_item():
206
+ config = VLESSConfig(
207
+ uuid="1c4b4bca-e3ff-4ca8-a062-6f399ad3cf45",
208
+ address="example.com",
209
+ port=443,
210
+ extra={"security": "tls"},
211
+ )
212
+
213
+ assert config.get_extra_item(
214
+ "security"
215
+ ) == "tls"
216
+ assert config.get_extra_item("missing") is None
217
+ assert config.get_extra_item(
218
+ "missing",
219
+ "default",
220
+ ) == "default"
221
+
222
+
223
+ def test_remove_extra_item():
224
+ config = VLESSConfig(
225
+ uuid="1c4b4bca-e3ff-4ca8-a062-6f399ad3cf45",
226
+ address="example.com",
227
+ port=443,
228
+ extra={"security": "tls"},
229
+ )
230
+
231
+ config.remove_extra_item(
232
+ "security"
233
+ )
234
+
235
+ assert config.extra == {}
236
+
237
+
171
238
  def test_update_methods():
172
239
  config = VLESSConfig(
173
240
  uuid="1c4b4bca-e3ff-4ca8-a062-6f399ad3cf45",