v2kit 0.2__tar.gz → 0.4__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 (42) hide show
  1. {v2kit-0.2 → v2kit-0.4}/AUTHORS.md +1 -1
  2. {v2kit-0.2 → v2kit-0.4}/CHANGELOG.md +20 -1
  3. {v2kit-0.2 → v2kit-0.4}/PKG-INFO +27 -5
  4. {v2kit-0.2 → v2kit-0.4}/README.md +5 -2
  5. {v2kit-0.2 → v2kit-0.4}/SECURITY.md +2 -2
  6. v2kit-0.2/dev-requirements.txt → v2kit-0.4/requirements-dev.txt +2 -0
  7. {v2kit-0.2 → v2kit-0.4}/setup.py +6 -16
  8. {v2kit-0.2 → v2kit-0.4}/tests/test_errors.py +60 -4
  9. {v2kit-0.2 → v2kit-0.4}/tests/test_functions.py +33 -13
  10. v2kit-0.4/tests/test_http.py +194 -0
  11. v2kit-0.4/tests/test_shadowsocks.py +196 -0
  12. v2kit-0.4/tests/test_socks.py +192 -0
  13. v2kit-0.4/tests/test_trojan.py +161 -0
  14. v2kit-0.4/tests/test_vless.py +169 -0
  15. v2kit-0.4/tests/test_vmess.py +163 -0
  16. {v2kit-0.2 → v2kit-0.4}/v2kit/__init__.py +8 -2
  17. {v2kit-0.2 → v2kit-0.4}/v2kit/functions.py +19 -1
  18. v2kit-0.4/v2kit/models/__init__.py +10 -0
  19. v2kit-0.4/v2kit/models/base.py +108 -0
  20. v2kit-0.4/v2kit/models/http.py +166 -0
  21. v2kit-0.4/v2kit/models/shadowsocks.py +168 -0
  22. v2kit-0.4/v2kit/models/socks.py +172 -0
  23. v2kit-0.4/v2kit/models/trojan.py +140 -0
  24. v2kit-0.4/v2kit/models/vless.py +141 -0
  25. v2kit-0.4/v2kit/models/vmess.py +217 -0
  26. {v2kit-0.2 → v2kit-0.4}/v2kit/params.py +3 -1
  27. {v2kit-0.2 → v2kit-0.4}/v2kit/parsers.py +43 -4
  28. {v2kit-0.2 → v2kit-0.4}/v2kit/utils.py +2 -3
  29. {v2kit-0.2 → v2kit-0.4}/v2kit/validators.py +12 -0
  30. {v2kit-0.2 → v2kit-0.4}/v2kit.egg-info/PKG-INFO +27 -5
  31. v2kit-0.4/v2kit.egg-info/SOURCES.txt +36 -0
  32. v2kit-0.2/requirements.txt +0 -1
  33. v2kit-0.2/tests/test_models.py +0 -114
  34. v2kit-0.2/v2kit/models.py +0 -735
  35. v2kit-0.2/v2kit.egg-info/SOURCES.txt +0 -25
  36. {v2kit-0.2 → v2kit-0.4}/LICENSE +0 -0
  37. {v2kit-0.2 → v2kit-0.4}/MANIFEST.in +0 -0
  38. {v2kit-0.2 → v2kit-0.4}/setup.cfg +0 -0
  39. {v2kit-0.2 → v2kit-0.4}/v2kit/__main__.py +0 -0
  40. {v2kit-0.2 → v2kit-0.4}/v2kit/errors.py +0 -0
  41. {v2kit-0.2 → v2kit-0.4}/v2kit.egg-info/dependency_links.txt +0 -0
  42. {v2kit-0.2 → v2kit-0.4}/v2kit.egg-info/top_level.txt +0 -0
@@ -5,7 +5,7 @@
5
5
 
6
6
  # Other Contributors
7
7
  ----------
8
- - [ChatGPT](https://chat.openai.com/) ++
8
+ - [Gemini](https://gemini.google.com) ++
9
9
 
10
10
  ++ Graphic designer
11
11
 
@@ -5,6 +5,23 @@ 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.4] - 2026-07-02
9
+ ### Added
10
+ - `HttpConfig` class
11
+ ### Changed
12
+ - `parse` function modified
13
+ - `README.md` updated
14
+ - Test system modified
15
+ ## [0.3] - 2026-06-16
16
+ ### Added
17
+ - Logo
18
+ - `SocksConfig` class
19
+ - `is_socks` function
20
+ ### Changed
21
+ - `README.md` updated
22
+ - Test system modified
23
+ - Dependencies structure modified
24
+ - All config models moved to `models` folder
8
25
  ## [0.2] - 2026-06-04
9
26
  ### Added
10
27
  - `VMESSConfig` class
@@ -33,7 +50,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
33
50
  - `encode_subscription` function
34
51
  - `decode_subscription` function
35
52
 
36
- [Unreleased]: https://github.com/sepandhaghighi/v2kit/compare/v0.2...dev
53
+ [Unreleased]: https://github.com/sepandhaghighi/v2kit/compare/v0.4...dev
54
+ [0.4]: https://github.com/sepandhaghighi/v2kit/compare/v0.3...v0.4
55
+ [0.3]: https://github.com/sepandhaghighi/v2kit/compare/v0.2...v0.3
37
56
  [0.2]: https://github.com/sepandhaghighi/v2kit/compare/v0.1...v0.2
38
57
  [0.1]: https://github.com/sepandhaghighi/v2kit/compare/bb5a2cb...v0.1
39
58
 
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: v2kit
3
- Version: 0.2
3
+ Version: 0.4
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.2
6
+ Download-URL: https://github.com/sepandhaghighi/v2kit/tarball/v0.4
7
7
  Author: Sepand Haghighi
8
8
  Author-email: me@sepand.tech
9
9
  License: MIT
@@ -44,6 +44,7 @@ Dynamic: summary
44
44
 
45
45
 
46
46
  <div align="center">
47
+ <img src="https://github.com/sepandhaghighi/v2kit/raw/main/otherfiles/logo.png" width="350">
47
48
  <h1>V2Kit: A Lightweight Toolkit for V2Ray Config Manipulation</h1>
48
49
  <br/>
49
50
  <a href="https://www.python.org/"><img src="https://img.shields.io/badge/built%20with-Python3-green.svg" alt="built with Python3"></a>
@@ -95,13 +96,13 @@ V2Kit is a lightweight and extensible Python toolkit for working with V2Ray prox
95
96
  ## Installation
96
97
 
97
98
  ### Source Code
98
- - Download [Version 0.2](https://github.com/sepandhaghighi/v2kit/archive/v0.2.zip) or [Latest Source](https://github.com/sepandhaghighi/v2kit/archive/dev.zip)
99
+ - Download [Version 0.4](https://github.com/sepandhaghighi/v2kit/archive/v0.4.zip) or [Latest Source](https://github.com/sepandhaghighi/v2kit/archive/dev.zip)
99
100
  - `pip install .`
100
101
 
101
102
  ### PyPI
102
103
 
103
104
  - Check [Python Packaging User Guide](https://packaging.python.org/installing/)
104
- - `pip install v2kit==0.2`
105
+ - `pip install v2kit==0.4`
105
106
 
106
107
  ## Supported Protocols
107
108
 
@@ -113,6 +114,8 @@ V2Kit currently supports the following protocols:
113
114
  | VLESS | ✅ | ✅ | ✅ | ✅ |
114
115
  | Trojan | ✅ | ✅ | ✅ | ✅ |
115
116
  | Shadowsocks | ✅ | ✅ | ✅ | ✅ |
117
+ | SOCKS | ✅ | ✅ | ✅ | ✅ |
118
+ | HTTP | ✅ | ✅ | ✅ | ✅ |
116
119
 
117
120
 
118
121
  ## Usage
@@ -208,6 +211,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
208
211
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
209
212
 
210
213
  ## [Unreleased]
214
+ ## [0.4] - 2026-07-02
215
+ ### Added
216
+ - `HttpConfig` class
217
+ ### Changed
218
+ - `parse` function modified
219
+ - `README.md` updated
220
+ - Test system modified
221
+ ## [0.3] - 2026-06-16
222
+ ### Added
223
+ - Logo
224
+ - `SocksConfig` class
225
+ - `is_socks` function
226
+ ### Changed
227
+ - `README.md` updated
228
+ - Test system modified
229
+ - Dependencies structure modified
230
+ - All config models moved to `models` folder
211
231
  ## [0.2] - 2026-06-04
212
232
  ### Added
213
233
  - `VMESSConfig` class
@@ -236,7 +256,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
236
256
  - `encode_subscription` function
237
257
  - `decode_subscription` function
238
258
 
239
- [Unreleased]: https://github.com/sepandhaghighi/v2kit/compare/v0.2...dev
259
+ [Unreleased]: https://github.com/sepandhaghighi/v2kit/compare/v0.4...dev
260
+ [0.4]: https://github.com/sepandhaghighi/v2kit/compare/v0.3...v0.4
261
+ [0.3]: https://github.com/sepandhaghighi/v2kit/compare/v0.2...v0.3
240
262
  [0.2]: https://github.com/sepandhaghighi/v2kit/compare/v0.1...v0.2
241
263
  [0.1]: https://github.com/sepandhaghighi/v2kit/compare/bb5a2cb...v0.1
242
264
 
@@ -1,4 +1,5 @@
1
1
  <div align="center">
2
+ <img src="https://github.com/sepandhaghighi/v2kit/raw/main/otherfiles/logo.png" width="350">
2
3
  <h1>V2Kit: A Lightweight Toolkit for V2Ray Config Manipulation</h1>
3
4
  <br/>
4
5
  <a href="https://www.python.org/"><img src="https://img.shields.io/badge/built%20with-Python3-green.svg" alt="built with Python3"></a>
@@ -50,13 +51,13 @@ V2Kit is a lightweight and extensible Python toolkit for working with V2Ray prox
50
51
  ## Installation
51
52
 
52
53
  ### Source Code
53
- - Download [Version 0.2](https://github.com/sepandhaghighi/v2kit/archive/v0.2.zip) or [Latest Source](https://github.com/sepandhaghighi/v2kit/archive/dev.zip)
54
+ - Download [Version 0.4](https://github.com/sepandhaghighi/v2kit/archive/v0.4.zip) or [Latest Source](https://github.com/sepandhaghighi/v2kit/archive/dev.zip)
54
55
  - `pip install .`
55
56
 
56
57
  ### PyPI
57
58
 
58
59
  - Check [Python Packaging User Guide](https://packaging.python.org/installing/)
59
- - `pip install v2kit==0.2`
60
+ - `pip install v2kit==0.4`
60
61
 
61
62
  ## Supported Protocols
62
63
 
@@ -68,6 +69,8 @@ V2Kit currently supports the following protocols:
68
69
  | VLESS | ✅ | ✅ | ✅ | ✅ |
69
70
  | Trojan | ✅ | ✅ | ✅ | ✅ |
70
71
  | Shadowsocks | ✅ | ✅ | ✅ | ✅ |
72
+ | SOCKS | ✅ | ✅ | ✅ | ✅ |
73
+ | HTTP | ✅ | ✅ | ✅ | ✅ |
71
74
 
72
75
 
73
76
  ## Usage
@@ -4,8 +4,8 @@
4
4
 
5
5
  | Version | Supported |
6
6
  | ------------- | ------------------ |
7
- | 0.2 | :white_check_mark: |
8
- | < 0.2 | :x: |
7
+ | 0.4 | :white_check_mark: |
8
+ | < 0.4 | :x: |
9
9
 
10
10
  ## Reporting a Vulnerability
11
11
 
@@ -4,3 +4,5 @@ setuptools>=40.8.0
4
4
  vulture>=1.0
5
5
  bandit>=1.5.1
6
6
  pydocstyle>=6.3.0
7
+ build>=1.1.1
8
+ twine>=4.0.2
@@ -1,16 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Setup module."""
3
- from typing import List
4
- try:
5
- from setuptools import setup
6
- except ImportError:
7
- from distutils.core import setup
8
-
9
-
10
- def get_requires() -> List[str]:
11
- """Read requirements.txt."""
12
- requirements = open("requirements.txt", "r").read()
13
- return list(filter(lambda x: x != "", requirements.split()))
3
+ from setuptools import setup
14
4
 
15
5
 
16
6
  def read_description() -> str:
@@ -32,8 +22,8 @@ def read_description() -> str:
32
22
 
33
23
  setup(
34
24
  name='v2kit',
35
- packages=['v2kit'],
36
- version='0.2',
25
+ packages=['v2kit', 'v2kit.models'],
26
+ version='0.4',
37
27
  description='V2Kit: A Lightweight Toolkit for V2Ray Config Manipulation',
38
28
  long_description=read_description(),
39
29
  long_description_content_type='text/markdown',
@@ -41,11 +31,11 @@ setup(
41
31
  author='Sepand Haghighi',
42
32
  author_email='me@sepand.tech',
43
33
  url='https://github.com/sepandhaghighi/v2kit',
44
- download_url='https://github.com/sepandhaghighi/v2kit/tarball/v0.2',
45
- keywords="v2ray v2ray-config v2ray-tools vmess vless trojan shadowsocks proxy subscription network",
34
+ download_url='https://github.com/sepandhaghighi/v2kit/tarball/v0.4',
35
+ keywords='v2ray v2ray-config v2ray-tools vmess vless trojan shadowsocks proxy subscription network',
46
36
  project_urls={
47
37
  'Source': 'https://github.com/sepandhaghighi/v2kit'},
48
- install_requires=get_requires(),
38
+ install_requires=[],
49
39
  python_requires='>=3.8',
50
40
  classifiers=[
51
41
  'Development Status :: 3 - Alpha',
@@ -1,16 +1,18 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  import pytest
4
- from v2kit import VMESSConfig, VLESSConfig, TrojanConfig, ShadowsocksConfig
4
+ from v2kit import VMESSConfig, VLESSConfig, TrojanConfig, ShadowsocksConfig, SocksConfig
5
5
  from v2kit import relabel, encode_subscription, decode_subscription, parse
6
6
  from v2kit import V2kitError, V2kitValidationError, V2kitParseError
7
7
  from v2kit.validators import _validate_uri
8
8
 
9
9
 
10
- INVALID_PROTOCOL = "http://example.com"
10
+ INVALID_PROTOCOL = "ftp://example.com"
11
11
 
12
12
  INVALID_VMESS = "vmess://invalid-base64"
13
13
 
14
+ INVALID_SHADOWSOCKS = "ss://invalid-base64@example.com:8388#ss-test"
15
+
14
16
  VALID_VLESS = (
15
17
  "vless://1c4b4bca-e3ff-4ca8-a062-6f399ad3cf45@example.com:443"
16
18
  "?security=tls#test"
@@ -61,7 +63,7 @@ def test_relabel_non_string_label():
61
63
  relabel(VALID_VLESS, 123)
62
64
 
63
65
 
64
- def test_encode_subscription_invalid_config():
66
+ def test_encode_subscription_invalid_config1():
65
67
  configs = [
66
68
  VALID_VLESS,
67
69
  "invalid-config",
@@ -71,6 +73,16 @@ def test_encode_subscription_invalid_config():
71
73
  encode_subscription(configs)
72
74
 
73
75
 
76
+ def test_encode_subscription_invalid_config2():
77
+ configs = [
78
+ VALID_VLESS,
79
+ 1,
80
+ ]
81
+
82
+ with pytest.raises(V2kitValidationError):
83
+ encode_subscription(configs)
84
+
85
+
74
86
  def test_decode_subscription_non_string():
75
87
  with pytest.raises(V2kitValidationError):
76
88
  decode_subscription(123)
@@ -109,7 +121,17 @@ def test_parse_invalid_uri():
109
121
 
110
122
  def test_parse_unsupported_protocol():
111
123
  with pytest.raises(V2kitParseError):
112
- parse("http://example.com")
124
+ parse(INVALID_PROTOCOL)
125
+
126
+
127
+ def test_parse_invalid_vmess():
128
+ with pytest.raises(V2kitParseError):
129
+ parse(INVALID_VMESS)
130
+
131
+
132
+ def test_parse_invalid_shadowsocks():
133
+ with pytest.raises(V2kitParseError):
134
+ parse(INVALID_SHADOWSOCKS)
113
135
 
114
136
 
115
137
  def test_vless_invalid_uuid():
@@ -166,3 +188,37 @@ def test_shadowsocks_empty_encryption():
166
188
  address="example.com",
167
189
  port=8388,
168
190
  )
191
+
192
+
193
+ def test_socks_invalid_port():
194
+ with pytest.raises(V2kitValidationError):
195
+ SocksConfig(
196
+ address="example.com",
197
+ port=70000,
198
+ )
199
+
200
+
201
+ def test_socks_empty_address():
202
+ with pytest.raises(V2kitValidationError):
203
+ SocksConfig(
204
+ address="",
205
+ port=1080,
206
+ )
207
+
208
+
209
+ def test_socks_invalid_username():
210
+ with pytest.raises(V2kitValidationError):
211
+ SocksConfig(
212
+ address="example.com",
213
+ port=1080,
214
+ username="",
215
+ )
216
+
217
+
218
+ def test_socks_invalid_password():
219
+ with pytest.raises(V2kitValidationError):
220
+ SocksConfig(
221
+ address="example.com",
222
+ port=1080,
223
+ password="",
224
+ )
@@ -1,9 +1,9 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  import json
4
- from v2kit import is_vmess, is_vless, is_trojan, is_shadowsocks, parse
4
+ from v2kit import is_vmess, is_vless, is_trojan, is_shadowsocks, is_socks, is_http, parse
5
5
  from v2kit import relabel, encode_subscription, decode_subscription
6
- from v2kit import VMESSConfig, VLESSConfig, TrojanConfig, ShadowsocksConfig
6
+ from v2kit import VMESSConfig, VLESSConfig, TrojanConfig, ShadowsocksConfig, SocksConfig
7
7
  from v2kit.utils import _encode_base64
8
8
 
9
9
 
@@ -29,19 +29,15 @@ def create_vmess(label="test"):
29
29
 
30
30
  VMESS_CONFIG = create_vmess()
31
31
 
32
- VLESS_CONFIG = (
33
- "vless://1c4b4bca-e3ff-4ca8-a062-6f399ad3cf45@example.com:443"
34
- "?security=tls#vless-test"
35
- )
32
+ VLESS_CONFIG = "vless://1c4b4bca-e3ff-4ca8-a062-6f399ad3cf45@example.com:443?security=tls#vless-test"
36
33
 
37
- TROJAN_CONFIG = (
38
- "trojan://password@example.com:443"
39
- "?security=tls#trojan-test"
40
- )
34
+ TROJAN_CONFIG = "trojan://password@example.com:443?security=tls#trojan-test"
41
35
 
42
- SHADOWSOCKS_CONFIG = (
43
- "ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ=@example.com:8388#ss-test"
44
- )
36
+ SHADOWSOCKS_CONFIG = "ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ=@example.com:8388#ss-test"
37
+
38
+ SOCKS_CONFIG = "socks://user:password@example.com:1080#socks-test"
39
+
40
+ HTTP_CONFIG = "http://user@example.com:1080#test"
45
41
 
46
42
 
47
43
  def test_is_vmess():
@@ -60,6 +56,10 @@ def test_is_shadowsocks():
60
56
  assert is_shadowsocks(SHADOWSOCKS_CONFIG) is True
61
57
 
62
58
 
59
+ def test_is_http():
60
+ assert is_http(HTTP_CONFIG) is True
61
+
62
+
63
63
  def test_is_vmess_negative():
64
64
  assert is_vmess(VLESS_CONFIG) is False
65
65
 
@@ -80,6 +80,18 @@ def test_is_shadowsocks_invalid():
80
80
  assert is_shadowsocks("invalid") is False
81
81
 
82
82
 
83
+ def test_is_http_invalid():
84
+ assert is_http("invalid") is False
85
+
86
+
87
+ def test_is_socks():
88
+ assert is_socks(SOCKS_CONFIG) is True
89
+
90
+
91
+ def test_is_socks_invalid():
92
+ assert is_socks("invalid") is False
93
+
94
+
83
95
  def test_relabel_vmess():
84
96
  result = relabel(VMESS_CONFIG, "new-label")
85
97
 
@@ -142,6 +154,7 @@ def test_encode_decode_subscription_roundtrip():
142
154
  VLESS_CONFIG,
143
155
  TROJAN_CONFIG,
144
156
  SHADOWSOCKS_CONFIG,
157
+ SOCKS_CONFIG,
145
158
  ]
146
159
 
147
160
  encoded = encode_subscription(configs)
@@ -206,3 +219,10 @@ def test_parse_shadowsocks():
206
219
  parse(SHADOWSOCKS_CONFIG),
207
220
  ShadowsocksConfig,
208
221
  )
222
+
223
+
224
+ def test_parse_socks():
225
+ assert isinstance(
226
+ parse(SOCKS_CONFIG),
227
+ SocksConfig,
228
+ )
@@ -0,0 +1,194 @@
1
+ # -*- coding: utf-8 -*-
2
+ import pytest
3
+ from v2kit import parse
4
+ from v2kit import HttpConfig
5
+
6
+
7
+ def test_defaults():
8
+ config = HttpConfig(
9
+ address="example.com",
10
+ port=1080,
11
+ )
12
+
13
+ assert config.label is None
14
+ assert config.extra == {}
15
+ assert config.username is None
16
+ assert config.password is None
17
+
18
+
19
+ def test_to_dict():
20
+ config = HttpConfig(
21
+ address="example.com",
22
+ port=1080,
23
+ username="user",
24
+ password="password",
25
+ label="test",
26
+ extra={"foo": "bar"},
27
+ )
28
+
29
+ assert config.to_dict() == {
30
+ "protocol": "http",
31
+ "address": "example.com",
32
+ "port": 1080,
33
+ "username": "user",
34
+ "password": "password",
35
+ "extra": {"foo": "bar"},
36
+ "label": "test",
37
+ }
38
+
39
+
40
+ def test_extra():
41
+ config = HttpConfig(
42
+ address="example.com",
43
+ port=1080,
44
+ extra={"foo": "bar"},
45
+ )
46
+
47
+ data = config.to_dict()
48
+
49
+ assert data["extra"]["foo"] == "bar"
50
+
51
+
52
+ def test_update_methods():
53
+ config = HttpConfig(
54
+ address="example.com",
55
+ port=1080,
56
+ )
57
+
58
+ config.update_address("example.org")
59
+ config.update_port(8080)
60
+ config.update_username("user")
61
+ config.update_password("password")
62
+
63
+ assert config.address == "example.org"
64
+ assert config.port == 8080
65
+ assert config.username == "user"
66
+ assert config.password == "password"
67
+
68
+
69
+ def test_update_extra():
70
+ config = HttpConfig(
71
+ address="example.com",
72
+ port=1080,
73
+ )
74
+
75
+ config.update_extra(
76
+ {"foo": "bar"}
77
+ )
78
+
79
+ assert config.extra["foo"] == "bar"
80
+
81
+
82
+ def test_method_chaining():
83
+ config = HttpConfig(
84
+ address="example.com",
85
+ port=1080,
86
+ )
87
+
88
+ config.update_address(
89
+ "example.org"
90
+ ).update_port(
91
+ 8080
92
+ ).update_username(
93
+ "user"
94
+ ).update_password(
95
+ "password"
96
+ )
97
+
98
+ assert config.address == "example.org"
99
+ assert config.port == 8080
100
+ assert config.username == "user"
101
+ assert config.password == "password"
102
+
103
+
104
+ @pytest.mark.parametrize(
105
+ "kwargs",
106
+ [
107
+ {"address": ""},
108
+ {"port": 0},
109
+ {"port": 1.2},
110
+ {"username": ""},
111
+ {"password": ""},
112
+ {"extra": 1},
113
+ ],
114
+ )
115
+ def test_invalid_values(kwargs):
116
+ params = {
117
+ "address": "example.com",
118
+ "port": 1080,
119
+ }
120
+ params.update(kwargs)
121
+
122
+ with pytest.raises(ValueError):
123
+ HttpConfig(**params)
124
+
125
+
126
+ def test_to_uri_roundtrip():
127
+ config = HttpConfig(
128
+ address="example.com",
129
+ port=1080,
130
+ username="user",
131
+ password="password",
132
+ label="test",
133
+ )
134
+
135
+ parsed = parse(config.to_uri())
136
+
137
+ assert parsed == config
138
+
139
+
140
+ def test_equality():
141
+ config1 = HttpConfig(
142
+ address="example.com",
143
+ port=1080,
144
+ username="user",
145
+ password="password",
146
+ )
147
+
148
+ config2 = HttpConfig(
149
+ address="example.com",
150
+ port=1080,
151
+ username="user",
152
+ password="password",
153
+ )
154
+
155
+ config3 = HttpConfig(
156
+ address="example.org",
157
+ port=1080,
158
+ username="user",
159
+ password="password",
160
+ )
161
+
162
+ assert config1 == config2
163
+ assert config1 != config3
164
+ assert config1 != "config1"
165
+
166
+
167
+ def test_to_uri_without_auth():
168
+ config = HttpConfig(
169
+ address="example.com",
170
+ port=1080,
171
+ label="test",
172
+ )
173
+
174
+ assert config.to_uri() == "http://example.com:1080#test"
175
+
176
+
177
+ def test_to_uri_username_only():
178
+ config = HttpConfig(
179
+ address="example.com",
180
+ port=1080,
181
+ username="user",
182
+ label="test",
183
+ )
184
+
185
+ assert config.to_uri() == "http://user@example.com:1080#test"
186
+
187
+
188
+ def test_repr():
189
+ config = HttpConfig(
190
+ address="example.com",
191
+ port=1080,
192
+ )
193
+
194
+ assert repr(config) == "HttpConfig(protocol=<Protocol.HTTP: 'http'>, label=None)"