aioetcd3 version grpc aio
This commit is contained in:
0
test/__init__.py
Normal file
0
test/__init__.py
Normal file
23
test/cfssl/ca.pem
Normal file
23
test/cfssl/ca.pem
Normal file
@@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID3jCCAsagAwIBAgIUfLIjRt7LfB0n9SVKLCfJR54lUdYwDQYJKoZIhvcNAQEL
|
||||
BQAwdTELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDVNhbiBGcmFuY2lzY28xCzAJBgNV
|
||||
BAcTAkNBMRgwFgYDVQQKEw9NeSBDb21wYW55IE5hbWUxEzARBgNVBAsTCk9yZyBV
|
||||
bml0IDIxEjAQBgNVBAMTCU15IG93biBDQTAeFw0xNzA4MDEwODM3MDBaFw0yMjA3
|
||||
MzEwODM3MDBaMHUxCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1TYW4gRnJhbmNpc2Nv
|
||||
MQswCQYDVQQHEwJDQTEYMBYGA1UEChMPTXkgQ29tcGFueSBOYW1lMRMwEQYDVQQL
|
||||
EwpPcmcgVW5pdCAyMRIwEAYDVQQDEwlNeSBvd24gQ0EwggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQDb8Ec7DVjLw74wmnG6Ke0DBXBKRxy2MVdQNw6a6vQ1
|
||||
UPXcjPjctPVoy1IrDK6f7CH3LGeiAv/g2zbgDQdRT/f3b986DxvBQMRj/rmRCsp4
|
||||
pcd+Nt0LtKBmKJCA7kk+urx/gmAS/9wa7RcC9kRg3husihIpa02AEMtd759Czjgy
|
||||
JHlCtlIoBSqxCqrEkKzc0Zw8SfDI7zKtOGlfA9bia6lx/y3TMvdCuPrDAvf1FSSj
|
||||
ECdXL70jYSSgA40VvhBVF5Nom/gsJ+/DmrYNwsGiA4klFp4ip4eKIyBcyk/Ni1uG
|
||||
wzQoLSaB0UqUCXKvWCimCzEnl/I0IJZ/TcrFmNoauGuxAgMBAAGjZjBkMA4GA1Ud
|
||||
DwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgECMB0GA1UdDgQWBBRsYfmUuh40
|
||||
o+DpLTJauE06g098JzAfBgNVHSMEGDAWgBRsYfmUuh40o+DpLTJauE06g098JzAN
|
||||
BgkqhkiG9w0BAQsFAAOCAQEAR/+35fG9b0Wt7jwoUF3L2A2OrZa36avb8ktkC/OO
|
||||
qm7skzUBDb7iz+iznEDICgrvecjXwq+te5ob92H3weDs6YJz0+T8EXBnUtzN2+bu
|
||||
eapdky5dZwweMqofr0FF2hLUVPWErgsZRj1gH1eLbFSirwtCbskmAzqK5TRKCtQL
|
||||
cOZ/WlsgmCdETzHSLztdKKTau1l/qHJBdH7hIppG4iEISMueHlW+H9+yu9haKu2L
|
||||
4J9feFOqC8G/aR+81og79WGwb2HJWgpw92ji8JxLvF5M1B++9AvwndkovkVgjFnk
|
||||
JBDagrwsg/gr+FVi3uw5NnktLgtzzcMD0VWGCmAEn/R3Mg==
|
||||
-----END CERTIFICATE-----
|
||||
5
test/cfssl/client-key.pem
Normal file
5
test/cfssl/client-key.pem
Normal file
@@ -0,0 +1,5 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIKIpaNwPGovuVxJM1GMGiJDHZJWaDzJyXeXPoIjGsJqfoAoGCCqGSM49
|
||||
AwEHoUQDQgAEYwsP1I307m9u1wrsqjacF5xdSk67iifUGT/MqbSSBtGlRK05VDn2
|
||||
87ghkIrsX1B7j/LJcUCDLnzmJVPjfa8lWg==
|
||||
-----END EC PRIVATE KEY-----
|
||||
5
test/cfssl/client-root-key.pem
Normal file
5
test/cfssl/client-root-key.pem
Normal file
@@ -0,0 +1,5 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIMD5KcGoWPEneh+YMw36oJqC8wwJHyTlP1saFYuqUSKGoAoGCCqGSM49
|
||||
AwEHoUQDQgAELMjGp4dyHA3vW3nU8XHh+JIT5B/bdIaSvVpJIoTgFPNoLpDspJmt
|
||||
GAxStBczUE8rwmRLfNbk0aG8zn8EsZoqDA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
19
test/cfssl/client-root.pem
Normal file
19
test/cfssl/client-root.pem
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC/TCCAeWgAwIBAgIUD8TzummshnrEovvmr4wqLzawxlowDQYJKoZIhvcNAQEL
|
||||
BQAwdTELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDVNhbiBGcmFuY2lzY28xCzAJBgNV
|
||||
BAcTAkNBMRgwFgYDVQQKEw9NeSBDb21wYW55IE5hbWUxEzARBgNVBAsTCk9yZyBV
|
||||
bml0IDIxEjAQBgNVBAMTCU15IG93biBDQTAeFw0xNzA4MDEwOTU5MDBaFw0yMjA3
|
||||
MzEwOTU5MDBaMEExCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1TYW4gRnJhbmNpc2Nv
|
||||
MQswCQYDVQQHEwJDQTENMAsGA1UEAxMEcm9vdDBZMBMGByqGSM49AgEGCCqGSM49
|
||||
AwEHA0IABCzIxqeHchwN71t51PFx4fiSE+Qf23SGkr1aSSKE4BTzaC6Q7KSZrRgM
|
||||
UrQXM1BPK8JkS3zW5NGhvM5/BLGaKgyjgYMwgYAwDgYDVR0PAQH/BAQDAgWgMBMG
|
||||
A1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFJZWw4xW
|
||||
lf9oa/ioycvblk/fzktLMB8GA1UdIwQYMBaAFGxh+ZS6HjSj4OktMlq4TTqDT3wn
|
||||
MAsGA1UdEQQEMAKCADANBgkqhkiG9w0BAQsFAAOCAQEAo4N4uDXl6nIG8ndrOeoe
|
||||
S2JpPTU+gkaez2fs21DpuGO3SLSEnIYLcaY3p6sdjU2m0m2yGweLKLfVmQLzHO0R
|
||||
4sZtKQFY1sklhCAhmiU5YZbb98gyXMfPVaFXCy5IWnajDsmhmh0G0UbVV/zaWJw+
|
||||
B+yzGVvWMBI4htG9Zz59yIt4Fku2TgqDudiFEzm9OB9LykYS+oKKLqb2DlLmWSdu
|
||||
NOr0j+sSwzTiGNSstb4jaXhbO2f80mykg4Rs5oFbiqYMH7qOfk5uR/uSomu+l03v
|
||||
cv9t71iCZ1/ss+ZgfS24crnsAUqUZBRmPGu1lrXRaVNzkK9BGZ5XAHT2eBEQAlqp
|
||||
OA==
|
||||
-----END CERTIFICATE-----
|
||||
19
test/cfssl/client.pem
Normal file
19
test/cfssl/client.pem
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC/zCCAeegAwIBAgIUfo2E4cRuVDSGcUlQbc8VWD0EHZcwDQYJKoZIhvcNAQEL
|
||||
BQAwdTELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDVNhbiBGcmFuY2lzY28xCzAJBgNV
|
||||
BAcTAkNBMRgwFgYDVQQKEw9NeSBDb21wYW55IE5hbWUxEzARBgNVBAsTCk9yZyBV
|
||||
bml0IDIxEjAQBgNVBAMTCU15IG93biBDQTAeFw0xNzA4MDEwOTU4MDBaFw0yMjA3
|
||||
MzEwOTU4MDBaMEMxCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1TYW4gRnJhbmNpc2Nv
|
||||
MQswCQYDVQQHEwJDQTEPMA0GA1UEAxMGY2xpZW50MFkwEwYHKoZIzj0CAQYIKoZI
|
||||
zj0DAQcDQgAEYwsP1I307m9u1wrsqjacF5xdSk67iifUGT/MqbSSBtGlRK05VDn2
|
||||
87ghkIrsX1B7j/LJcUCDLnzmJVPjfa8lWqOBgzCBgDAOBgNVHQ8BAf8EBAMCBaAw
|
||||
EwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUG6Sc
|
||||
mNr0LfHR3Th2ER8mo8UFOSIwHwYDVR0jBBgwFoAUbGH5lLoeNKPg6S0yWrhNOoNP
|
||||
fCcwCwYDVR0RBAQwAoIAMA0GCSqGSIb3DQEBCwUAA4IBAQAk5+gTElEnkaKRIuy2
|
||||
Uf/8GRSFAmlCbuEVGGuQ4Iif1KWVt0sklUC4EpknJGCDBDfRlH/n/O0cIAhxHJVd
|
||||
8HXRfYy+ynhr08gdE7lbueavEpvUb3QNFR8ZrODcqwvJgHyWsffk7f87hpsx9lr2
|
||||
mfLvokau0UhpVlq+x7IQ5dKw/ZzKv/zjI/A2guwK1UWdk5vr0W7LE5XY4pa+9/Qy
|
||||
y6FxHcKc4z8FJ6ClRGy/RGJrbg0VgCmrTMa7NCpIyZ/onn7RaxHatSn7cnQPVNKq
|
||||
fBHHBVdl1LlsJSPRZar0zGkUS1UeCtcSW1aqSmkO39p18tJ9hDWM0xkY6FlI3A+4
|
||||
shAQ
|
||||
-----END CERTIFICATE-----
|
||||
5
test/cfssl/server-key.pem
Normal file
5
test/cfssl/server-key.pem
Normal file
@@ -0,0 +1,5 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIKiO/qjfQEIJS4OYW3rHUQodAEk2/PzNj+V6Oy/+JSnIoAoGCCqGSM49
|
||||
AwEHoUQDQgAE2azbBMQaqhuTm5d+7rzIdqlXnBrv0LxwDLIQnUTosRYlrS19+gEg
|
||||
AccoyJEyzGFzd3+Ot6OOX3nXUxDSVFzlLA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
19
test/cfssl/server.pem
Normal file
19
test/cfssl/server.pem
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDFjCCAf6gAwIBAgIUHeUTcT0g5zmaUzPKeqEi6AZMZ5wwDQYJKoZIhvcNAQEL
|
||||
BQAwdTELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDVNhbiBGcmFuY2lzY28xCzAJBgNV
|
||||
BAcTAkNBMRgwFgYDVQQKEw9NeSBDb21wYW55IE5hbWUxEzARBgNVBAsTCk9yZyBV
|
||||
bml0IDIxEjAQBgNVBAMTCU15IG93biBDQTAeFw0xNzA4MDIwODM4MDBaFw0yMjA4
|
||||
MDEwODM4MDBaMEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1TYW4gRnJhbmNpc2Nv
|
||||
MQswCQYDVQQHEwJDQTEQMA4GA1UEAxMHbWVtYmVyMzBZMBMGByqGSM49AgEGCCqG
|
||||
SM49AwEHA0IABNms2wTEGqobk5uXfu68yHapV5wa79C8cAyyEJ1E6LEWJa0tffoB
|
||||
IAHHKMiRMsxhc3d/jrejjl9511MQ0lRc5SyjgZkwgZYwDgYDVR0PAQH/BAQDAgWg
|
||||
MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKmG
|
||||
Ok0gF06fFZlNPVR8QoF3i7/XMB8GA1UdIwQYMBaAFGxh+ZS6HjSj4OktMlq4TTqD
|
||||
T3wnMCEGA1UdEQQaMBiHBH8AAAGHBGRJLgOHBGRJLgSHBGRJLgUwDQYJKoZIhvcN
|
||||
AQELBQADggEBAMOUiyg+R6Qh6CFmNjxZJCk1hlRPrYucA/7phWRsqOI/PCFpak8d
|
||||
LIOCQdPwTC20hqZxszN0GmJVdIZy1FEDa7yDZip2pYrmCR2ePAAwXtEhUAGBblIU
|
||||
IAFiLtFps5GQqi3+f9SUto0HnriQiBt8fSf4KNeD84gkOt524qliAY2bIOdphJaY
|
||||
Y+Qg6jkn8+lzoY+rD8tzOx37dgL+/V0dCyo7fBLnW6mOEdHk5sMpPPyWzmCYSdeb
|
||||
SqNpp9+EDOVhz175CQuuq0WD+x3D210UIQsEFvyjg/W6Vinl9W/hOG6yxv01IVcM
|
||||
yhngfepPKZ20b4njhHJv+geOn5yshCcTQjI=
|
||||
-----END CERTIFICATE-----
|
||||
279
test/test_auth.py
Normal file
279
test/test_auth.py
Normal file
@@ -0,0 +1,279 @@
|
||||
import unittest
|
||||
import asyncio
|
||||
import functools
|
||||
|
||||
from aioetcd3.client import client, ssl_client, set_grpc_cipher
|
||||
from aioetcd3.help import range_all, PER_RW
|
||||
from aioetcd3.exceptions import AuthError, Unauthenticated, PermissionDenied
|
||||
|
||||
from .utils import switch_auth_off, switch_auth_on
|
||||
|
||||
|
||||
def asynctest(f):
|
||||
@functools.wraps(f)
|
||||
def _f(self):
|
||||
asyncio.get_event_loop().run_until_complete(f(self))
|
||||
|
||||
return _f
|
||||
|
||||
|
||||
TEST_USER_NAME = 'test'
|
||||
TEST_USER_PASSWORD = "test"
|
||||
TEST_ROLE_NAME = 'admin'
|
||||
|
||||
|
||||
class AuthTest(unittest.TestCase):
|
||||
@asynctest
|
||||
async def setUp(self):
|
||||
endpoints = "127.0.0.1:2379"
|
||||
self.client = client(endpoint=endpoints)
|
||||
|
||||
set_grpc_cipher()
|
||||
auth_etcd_url = "127.0.0.1:2378"
|
||||
self.root_client = ssl_client(endpoint=auth_etcd_url, ca_file="test/cfssl/ca.pem",
|
||||
cert_file="test/cfssl/client-root.pem",
|
||||
key_file="test/cfssl/client-root-key.pem")
|
||||
|
||||
self.client_client = ssl_client(endpoint=auth_etcd_url, ca_file="test/cfssl/ca.pem",
|
||||
cert_file="test/cfssl/client.pem",
|
||||
key_file="test/cfssl/client-key.pem")
|
||||
|
||||
await self.cleanUp()
|
||||
|
||||
@asynctest
|
||||
async def test_auth_1(self):
|
||||
|
||||
await self.client.user_add(username=TEST_USER_NAME, password='1234')
|
||||
users = await self.client.user_list()
|
||||
|
||||
self.assertIn(TEST_USER_NAME, users)
|
||||
|
||||
roles = await self.client.user_get(username=TEST_USER_NAME)
|
||||
self.assertEqual(len(roles), 0)
|
||||
|
||||
await self.client.user_change_password(username=TEST_USER_NAME, password=TEST_USER_PASSWORD)
|
||||
|
||||
await self.client.user_delete(username=TEST_USER_NAME)
|
||||
|
||||
@asynctest
|
||||
async def test_auth_2(self):
|
||||
|
||||
await self.client.role_add(name=TEST_ROLE_NAME)
|
||||
|
||||
roles = await self.client.role_list()
|
||||
self.assertIn(TEST_ROLE_NAME, roles)
|
||||
|
||||
role_info = await self.client.role_get(name=TEST_ROLE_NAME)
|
||||
|
||||
await self.client.role_delete(name=TEST_ROLE_NAME)
|
||||
|
||||
@asynctest
|
||||
async def test_auth_3(self):
|
||||
|
||||
await self.client.user_add(username=TEST_USER_NAME, password=TEST_USER_PASSWORD)
|
||||
with self.assertRaises(Exception):
|
||||
await self.client.user_grant_role(username=TEST_USER_NAME, role=TEST_ROLE_NAME)
|
||||
|
||||
await self.client.role_add(name=TEST_ROLE_NAME)
|
||||
await self.client.user_grant_role(username=TEST_USER_NAME, role=TEST_ROLE_NAME)
|
||||
|
||||
await self.client.role_grant_permission(name=TEST_ROLE_NAME,
|
||||
key_range=range_all(),
|
||||
permission=PER_RW)
|
||||
|
||||
await self.client.user_revoke_role(username=TEST_USER_NAME, role=TEST_ROLE_NAME)
|
||||
|
||||
await self.client.role_revoke_permission(name=TEST_ROLE_NAME,
|
||||
key_range=range_all())
|
||||
|
||||
|
||||
@asynctest
|
||||
async def test_auth_4(self):
|
||||
await self.root_client.user_add(username='root', password='root')
|
||||
await self.root_client.role_add(name='root')
|
||||
await self.root_client.user_grant_role(username='root', role='root')
|
||||
await self.root_client.auth_enable()
|
||||
|
||||
await self.root_client.user_add(username='client', password='client')
|
||||
await self.root_client.role_add(name='client')
|
||||
|
||||
await self.root_client.put('/foo', '/foo')
|
||||
value, meta = await self.root_client.get('/foo')
|
||||
self.assertEqual(value, b'/foo')
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
await self.client_client.get('/foo')
|
||||
|
||||
await self.root_client.role_grant_permission(name='client', key_range='/foo', permission=PER_RW)
|
||||
await self.root_client.user_grant_role(username='client', role='client')
|
||||
|
||||
value, meta = await self.client_client.get('/foo')
|
||||
self.assertEqual(value, b'/foo')
|
||||
|
||||
await self.client_client.put('/foo', 'ssss')
|
||||
|
||||
async def delete_all_user(self):
|
||||
users = await self.client.user_list()
|
||||
|
||||
for u in users:
|
||||
await self.client.user_delete(username=u)
|
||||
|
||||
users = await self.root_client.user_list()
|
||||
|
||||
for u in users:
|
||||
await self.root_client.user_delete(username=u)
|
||||
|
||||
async def delete_all_role(self):
|
||||
roles = await self.client.role_list()
|
||||
|
||||
for r in roles:
|
||||
await self.client.role_delete(name=r)
|
||||
|
||||
roles = await self.root_client.role_list()
|
||||
|
||||
for r in roles:
|
||||
await self.root_client.role_delete(name=r)
|
||||
|
||||
async def cleanUp(self):
|
||||
|
||||
await self.client.delete(range_all())
|
||||
|
||||
await self.root_client.auth_disable()
|
||||
|
||||
await self.delete_all_user()
|
||||
await self.delete_all_role()
|
||||
|
||||
@asynctest
|
||||
async def tearDown(self):
|
||||
await self.cleanUp()
|
||||
await self.client.close()
|
||||
|
||||
|
||||
class PasswordAuthTest(unittest.TestCase):
|
||||
@asynctest
|
||||
async def setUp(self):
|
||||
self.endpoints = "127.0.0.1:2379"
|
||||
self.unauthenticated_client = client(endpoint=self.endpoints)
|
||||
await self.cleanUp()
|
||||
await switch_auth_on(self.unauthenticated_client)
|
||||
self.client_client = client(
|
||||
endpoint=self.endpoints, username="client", password="client"
|
||||
)
|
||||
self.root_client = client(endpoint=self.endpoints, username="root", password="root")
|
||||
|
||||
async def create_kv_for_test(self):
|
||||
await self.root_client.put('/foo', '/foo')
|
||||
value, meta = await self.root_client.get('/foo')
|
||||
self.assertEqual(value, b'/foo')
|
||||
|
||||
@asynctest
|
||||
async def test_auth_1(self):
|
||||
await self.create_kv_for_test()
|
||||
|
||||
with self.assertRaises(PermissionDenied):
|
||||
await self.client_client.get('/foo')
|
||||
|
||||
await self.root_client.role_grant_permission(name='client', key_range='/foo', permission=PER_RW)
|
||||
value, meta = await self.client_client.get('/foo')
|
||||
self.assertEqual(value, b'/foo')
|
||||
|
||||
await self.client_client.put('/foo', 'ssss')
|
||||
|
||||
@asynctest
|
||||
async def test_wrong_password(self):
|
||||
wrong_password_client = client(endpoint=self.endpoints, username="client", password="wrong_password")
|
||||
with self.assertRaises(AuthError) as exc:
|
||||
await wrong_password_client.get("/foo")
|
||||
assert repr(exc.exception) == "`{}`: reason: `{}`".format(exc.exception.code, exc.exception.details)
|
||||
|
||||
@asynctest
|
||||
async def test_wrong_token(self):
|
||||
await self.create_kv_for_test()
|
||||
await self.root_client.role_grant_permission(name='client', key_range='/foo', permission=PER_RW)
|
||||
|
||||
new_client = client(endpoint=self.endpoints, username="client", password="client")
|
||||
value, meta = await self.client_client.get('/foo')
|
||||
self.assertEqual(value, b'/foo')
|
||||
|
||||
# Put invalid token
|
||||
new_client._metadata = (("token", "invalid_token"),)
|
||||
with self.assertRaises(Unauthenticated) as exc:
|
||||
await new_client.get("/foo")
|
||||
|
||||
async def cleanUp(self):
|
||||
await self.unauthenticated_client.delete(range_all())
|
||||
|
||||
@asynctest
|
||||
async def tearDown(self):
|
||||
await switch_auth_off(self.root_client, self.unauthenticated_client)
|
||||
await self.cleanUp()
|
||||
|
||||
|
||||
class PasswordAuthWithSslTest(unittest.TestCase):
|
||||
@asynctest
|
||||
async def setUp(self):
|
||||
self.endpoints = "127.0.0.1:2377"
|
||||
self.unauthenticated_client = ssl_client(
|
||||
endpoint=self.endpoints,
|
||||
ca_file="test/cfssl/ca.pem",
|
||||
)
|
||||
await self.cleanUp()
|
||||
await switch_auth_on(self.unauthenticated_client)
|
||||
self.root_client = ssl_client(endpoint=self.endpoints, ca_file="test/cfssl/ca.pem",
|
||||
username="root", password="root")
|
||||
|
||||
self.client_client = ssl_client(endpoint=self.endpoints, ca_file="test/cfssl/ca.pem",
|
||||
username="client", password="client")
|
||||
|
||||
async def create_kv_for_test(self):
|
||||
await self.root_client.put('/foo', '/foo')
|
||||
value, meta = await self.root_client.get('/foo')
|
||||
self.assertEqual(value, b'/foo')
|
||||
|
||||
@asynctest
|
||||
async def test_auth_1(self):
|
||||
await self.create_kv_for_test()
|
||||
|
||||
with self.assertRaises(PermissionDenied):
|
||||
await self.client_client.get('/foo')
|
||||
|
||||
await self.root_client.role_grant_permission(name='client', key_range='/foo', permission=PER_RW)
|
||||
value, meta = await self.client_client.get('/foo')
|
||||
self.assertEqual(value, b'/foo')
|
||||
|
||||
await self.client_client.put('/foo', 'ssss')
|
||||
|
||||
@asynctest
|
||||
async def test_wrong_password(self):
|
||||
wrong_password_client = ssl_client(
|
||||
endpoint=self.endpoints, ca_file="test/cfssl/ca.pem",
|
||||
username="client", password="wrong_password"
|
||||
)
|
||||
with self.assertRaises(AuthError) as exc:
|
||||
await wrong_password_client.get("/foo")
|
||||
assert repr(exc.exception) == "`{}`: reason: `{}`".format(exc.exception.code, exc.exception.details)
|
||||
|
||||
@asynctest
|
||||
async def test_wrong_token(self):
|
||||
await self.create_kv_for_test()
|
||||
await self.root_client.role_grant_permission(name='client', key_range='/foo', permission=PER_RW)
|
||||
|
||||
new_client = ssl_client(
|
||||
endpoint=self.endpoints, ca_file="test/cfssl/ca.pem",
|
||||
username="root", password="root"
|
||||
)
|
||||
value, meta = await new_client.get('/foo')
|
||||
self.assertEqual(value, b'/foo')
|
||||
|
||||
# Put invalid token
|
||||
new_client._metadata = (("token", "invalid_token"),)
|
||||
with self.assertRaises(Unauthenticated) as exc:
|
||||
await new_client.get("/foo")
|
||||
|
||||
async def cleanUp(self):
|
||||
await self.unauthenticated_client.delete(range_all())
|
||||
|
||||
@asynctest
|
||||
async def tearDown(self):
|
||||
await switch_auth_off(self.root_client, self.unauthenticated_client)
|
||||
await self.cleanUp()
|
||||
41
test/test_cluster.py
Normal file
41
test/test_cluster.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import unittest
|
||||
import asyncio
|
||||
import functools
|
||||
|
||||
from aioetcd3.client import client
|
||||
from aioetcd3.help import range_all
|
||||
|
||||
|
||||
def asynctest(f):
|
||||
@functools.wraps(f)
|
||||
def _f(self):
|
||||
asyncio.get_event_loop().run_until_complete(f(self))
|
||||
|
||||
return _f
|
||||
|
||||
|
||||
class ClusterTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
endpoints = "127.0.0.1:2379"
|
||||
self.client = client(endpoint=endpoints)
|
||||
|
||||
@asynctest
|
||||
async def test_member(self):
|
||||
members = await self.client.member_list()
|
||||
self.assertTrue(members)
|
||||
|
||||
m = members[0]
|
||||
# urls = [u for u in m.clientURLs]
|
||||
# urls = [u.rpartition("//")[2] for u in urls]
|
||||
|
||||
healthy, unhealthy = await self.client.member_healthy([m.clientURLs])
|
||||
self.assertTrue(healthy)
|
||||
self.assertFalse(unhealthy)
|
||||
|
||||
healthy, unhealthy = await self.client.member_healthy()
|
||||
self.assertTrue(healthy)
|
||||
self.assertFalse(unhealthy)
|
||||
|
||||
@asynctest
|
||||
async def tearDown(self):
|
||||
await self.client.close()
|
||||
163
test/test_kv.py
Normal file
163
test/test_kv.py
Normal file
@@ -0,0 +1,163 @@
|
||||
import unittest
|
||||
import asyncio
|
||||
import functools
|
||||
from aioetcd3.client import client
|
||||
from aioetcd3.kv import KV
|
||||
from aioetcd3.help import range_all, range_prefix, range_greater, range_greater_equal
|
||||
from aioetcd3 import transaction
|
||||
|
||||
|
||||
def asynctest(f):
|
||||
@functools.wraps(f)
|
||||
def _f(self):
|
||||
return asyncio.get_event_loop().run_until_complete(f(self))
|
||||
|
||||
return _f
|
||||
|
||||
|
||||
class KVTest(unittest.TestCase):
|
||||
|
||||
@asynctest
|
||||
async def setUp(self):
|
||||
endpoints = "127.0.0.1:2379"
|
||||
self.client = client(endpoint=endpoints)
|
||||
endpoints = "127.0.0.1:2379"
|
||||
self.client.update_server_list(endpoint=endpoints)
|
||||
await self.cleanUp()
|
||||
|
||||
async def cleanUp(self):
|
||||
await self.client.delete(key_range=range_all())
|
||||
|
||||
@asynctest
|
||||
async def tearDown(self):
|
||||
await self.cleanUp()
|
||||
await self.client.close()
|
||||
|
||||
@asynctest
|
||||
async def test_put_get(self):
|
||||
|
||||
for i in range(0, 10):
|
||||
key = '/test' + str(i)
|
||||
value, meta = await self.client.put(key, str(i))
|
||||
self.assertIsNone(value)
|
||||
self.assertIsNone(meta)
|
||||
|
||||
value, meta = await self.client.put('/test9', "10", prev_kv=True)
|
||||
self.assertEqual(value, b'9')
|
||||
self.assertIsNotNone(meta)
|
||||
|
||||
value, meta = await self.client.put('/test9', "9", prev_kv=True, ignore_value=True)
|
||||
self.assertEqual(value, b'10')
|
||||
self.assertIsNotNone(meta)
|
||||
|
||||
value, meta = await self.client.put('/test9', "9", prev_kv=True)
|
||||
self.assertEqual(value, b'10')
|
||||
self.assertIsNotNone(meta)
|
||||
|
||||
count = await self.client.count(key_range=range_all())
|
||||
self.assertEqual(count, 10)
|
||||
|
||||
value, meta = await self.client.get("/test9")
|
||||
self.assertEqual(value, b'9')
|
||||
self.assertIsNotNone(meta)
|
||||
|
||||
keys_list = await self.client.range_keys(key_range=range_all())
|
||||
self.assertEqual(len(keys_list), 10)
|
||||
|
||||
value_list = await self.client.range(key_range=range_all())
|
||||
self.assertEqual(len(value_list), 10)
|
||||
value = [v[1].decode('utf-8') for v in value_list]
|
||||
value.sort()
|
||||
real_value = [str(i) for i in range(0, 10)]
|
||||
self.assertEqual(value, real_value)
|
||||
|
||||
value_list = await self.client.range(key_range=range_all(), limit=5)
|
||||
self.assertEqual(len(value_list), 5)
|
||||
|
||||
value_list = await self.client.range(key_range=range_prefix('/'))
|
||||
self.assertEqual(len(value_list), 10)
|
||||
|
||||
value_list = await self.client.range(key_range=range_prefix('/'), limit=11)
|
||||
self.assertEqual(len(value_list), 10)
|
||||
|
||||
value_list = await self.client.range(key_range=range_greater_equal('/test8'))
|
||||
self.assertEqual(len(value_list), 2)
|
||||
self.assertEqual(value_list[0][1], b'8')
|
||||
self.assertEqual(value_list[1][1], b'9')
|
||||
|
||||
value_list = await self.client.range(key_range=range_greater('/testa'))
|
||||
self.assertEqual(len(value_list), 0)
|
||||
|
||||
await self.client.delete(key_range='/test9')
|
||||
value, meta = await self.client.get("/test9")
|
||||
self.assertIsNone(value)
|
||||
self.assertIsNone(meta)
|
||||
|
||||
value_list = await self.client.pop(key_range='/test8')
|
||||
self.assertEqual(len(value_list), 1)
|
||||
self.assertEqual(value_list[0][0], b'/test8')
|
||||
self.assertEqual(value_list[0][1], b'8')
|
||||
|
||||
value_list = await self.client.delete(key_range=range_prefix('/'), prev_kv=True)
|
||||
self.assertEqual(len(value_list), 8)
|
||||
|
||||
@asynctest
|
||||
async def test_transaction(self):
|
||||
await self.client.put('/trans1', 'trans1')
|
||||
await self.client.put('/trans2', 'trans2')
|
||||
|
||||
is_success, response = await self.client.txn(compare=[
|
||||
transaction.Value('/trans1') == b'trans1',
|
||||
transaction.Value('/trans2') == b'trans2'
|
||||
], success=[
|
||||
KV.get.txn('/trans1'),
|
||||
KV.range.txn('/trans2')
|
||||
], fail=[
|
||||
KV.delete.txn('/trans1')
|
||||
])
|
||||
|
||||
self.assertEqual(is_success, True)
|
||||
self.assertEqual(len(response), 2)
|
||||
|
||||
self.assertEqual(response[0][0], b'trans1')
|
||||
self.assertEqual(response[1][0][:2], (b'/trans2', b'trans2'))
|
||||
|
||||
is_success, response = await self.client.txn(compare=[
|
||||
transaction.Value('/trans1') == b'trans1',
|
||||
transaction.Value('/trans2') == b'trans2'
|
||||
], success=[
|
||||
KV.delete.txn('/trans1'),
|
||||
KV.put.txn('/trans2', 'trans2', prev_kv=True),
|
||||
KV.put.txn('/trans3', 'trans3', prev_kv=True)
|
||||
], fail=[
|
||||
KV.delete.txn('/trans1')
|
||||
])
|
||||
|
||||
self.assertEqual(is_success, True)
|
||||
self.assertEqual(len(response), 3)
|
||||
del_response = response[0]
|
||||
self.assertEqual(del_response, 1)
|
||||
put_response = response[1]
|
||||
self.assertEqual(put_response[0], b'trans2')
|
||||
put_response = response[2]
|
||||
# there is not pre_kv None
|
||||
self.assertIsNone(put_response[0])
|
||||
|
||||
is_success, response = await self.client.txn(compare=[
|
||||
transaction.Value('/trans3') != b'trans3',
|
||||
transaction.Version('/trans3') < 1000,
|
||||
transaction.Mod('/trans3') > 100,
|
||||
transaction.Create('/trans3') != 200
|
||||
], success=[
|
||||
], fail=[
|
||||
KV.delete.txn('/trans3', prev_kv=True)
|
||||
])
|
||||
|
||||
self.assertEqual(is_success, False)
|
||||
self.assertEqual(len(response), 1)
|
||||
self.assertEqual(len(response[0]), 1)
|
||||
self.assertEqual(response[0][0][:2], (b'/trans3', b'trans3'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
133
test/test_lease.py
Normal file
133
test/test_lease.py
Normal file
@@ -0,0 +1,133 @@
|
||||
import unittest
|
||||
import asyncio
|
||||
import functools
|
||||
|
||||
from aioetcd3.client import client
|
||||
from aioetcd3.help import range_all, range_prefix, PER_RW
|
||||
|
||||
from .utils import switch_auth_on, switch_auth_off
|
||||
|
||||
|
||||
def asynctest(f):
|
||||
@functools.wraps(f)
|
||||
def _f(self):
|
||||
asyncio.get_event_loop().run_until_complete(f(self))
|
||||
|
||||
return _f
|
||||
|
||||
|
||||
class LeaseTest(unittest.TestCase):
|
||||
@asynctest
|
||||
async def setUp(self):
|
||||
self.endpoints = "127.0.0.1:2379"
|
||||
self.client = client(endpoint=self.endpoints)
|
||||
|
||||
await self.cleanUp()
|
||||
|
||||
async def _lease_1(self):
|
||||
lease = await self.client.grant_lease(ttl=5)
|
||||
self.assertEqual(lease.ttl, 5)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
lease, keys = await self.client.get_lease_info(lease)
|
||||
self.assertLessEqual(lease.ttl, 4)
|
||||
self.assertEqual(len(keys), 0)
|
||||
|
||||
lease = await self.client.refresh_lease(lease)
|
||||
self.assertEqual(lease.ttl, 5)
|
||||
|
||||
await self.client.revoke_lease(lease)
|
||||
|
||||
lease, keys = await self.client.get_lease_info(lease)
|
||||
self.assertIsNone(lease)
|
||||
self.assertEqual(len(keys), 0)
|
||||
|
||||
@asynctest
|
||||
async def test_lease_1(self):
|
||||
await self._lease_1()
|
||||
|
||||
async def _lease_2(self):
|
||||
lease = await self.client.grant_lease(ttl=5)
|
||||
self.assertEqual(lease.ttl, 5)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
lease, keys = await lease.info()
|
||||
self.assertLessEqual(lease.ttl, 4)
|
||||
self.assertEqual(len(keys), 0)
|
||||
|
||||
lease = await lease.refresh()
|
||||
self.assertEqual(lease.ttl, 5)
|
||||
|
||||
await lease.revoke()
|
||||
lease, keys = await lease.info()
|
||||
self.assertIsNone(lease)
|
||||
self.assertEqual(len(keys), 0)
|
||||
|
||||
lease = None
|
||||
async with self.client.grant_lease_scope(ttl=5) as l:
|
||||
lease = l
|
||||
await asyncio.sleep(1)
|
||||
|
||||
lease, keys = await lease.info()
|
||||
self.assertIsNone(lease)
|
||||
self.assertEqual(len(keys), 0)
|
||||
|
||||
@asynctest
|
||||
async def test_lease_2(self):
|
||||
await self._lease_2()
|
||||
|
||||
async def _lease_3(self):
|
||||
lease = await self.client.grant_lease(ttl=5)
|
||||
self.assertEqual(lease.ttl, 5)
|
||||
|
||||
await self.client.put("/testlease", "testlease", lease=lease)
|
||||
|
||||
await asyncio.sleep(6)
|
||||
lease, keys = await lease.info()
|
||||
self.assertIsNone(lease, None)
|
||||
self.assertEqual(len(keys), 0)
|
||||
|
||||
value, meta = await self.client.get('/testlease')
|
||||
self.assertIsNone(value)
|
||||
self.assertIsNone(meta)
|
||||
|
||||
@asynctest
|
||||
async def test_lease_3(self):
|
||||
await self._lease_3()
|
||||
|
||||
async def _run_test_with_auth(self, test):
|
||||
default_client = self.client
|
||||
await switch_auth_on(default_client)
|
||||
root_client = client(endpoint=self.endpoints, username="root", password="root")
|
||||
await root_client.role_grant_permission(name='client', key_range=range_prefix('/testlease'), permission=PER_RW)
|
||||
self.client = client(endpoint=self.endpoints, username="client", password="client")
|
||||
try:
|
||||
await test()
|
||||
finally:
|
||||
await switch_auth_off(
|
||||
root_client,
|
||||
default_client
|
||||
)
|
||||
await root_client.close()
|
||||
await self.client.close()
|
||||
self.client = default_client
|
||||
|
||||
@asynctest
|
||||
async def test_lease_1_with_auth(self):
|
||||
await self._run_test_with_auth(self._lease_1)
|
||||
|
||||
@asynctest
|
||||
async def test_lease_2_with_auth(self):
|
||||
await self._run_test_with_auth(self._lease_2)
|
||||
|
||||
@asynctest
|
||||
async def test_lease_3_with_auth(self):
|
||||
await self._run_test_with_auth(self._lease_3)
|
||||
|
||||
@asynctest
|
||||
async def tearDown(self):
|
||||
await self.cleanUp()
|
||||
await self.client.close()
|
||||
|
||||
async def cleanUp(self):
|
||||
await self.client.delete(range_all())
|
||||
410
test/test_watch.py
Normal file
410
test/test_watch.py
Normal file
@@ -0,0 +1,410 @@
|
||||
import unittest
|
||||
import functools
|
||||
import asyncio
|
||||
from grpc import RpcError
|
||||
|
||||
from aioetcd3.client import client
|
||||
from aioetcd3.help import range_all, range_prefix, PER_RW
|
||||
from aioetcd3.watch import EVENT_TYPE_CREATE,EVENT_TYPE_DELETE,EVENT_TYPE_MODIFY,\
|
||||
CompactRevisonException, WatchException
|
||||
|
||||
from .utils import switch_auth_off, switch_auth_on
|
||||
|
||||
|
||||
def asynctest(f):
|
||||
@functools.wraps(f)
|
||||
def _f(self):
|
||||
return asyncio.get_event_loop().run_until_complete(f(self))
|
||||
|
||||
return _f
|
||||
|
||||
|
||||
class WatchTest(unittest.TestCase):
|
||||
@asynctest
|
||||
async def setUp(self):
|
||||
self.endpoints = "127.0.0.1:2379"
|
||||
self.client = client(endpoint=self.endpoints)
|
||||
await self.cleanUp()
|
||||
|
||||
async def common_watch1(self):
|
||||
f1 = asyncio.get_event_loop().create_future()
|
||||
|
||||
async def watch_1():
|
||||
i = 0
|
||||
async with self.client.watch_scope('/foo') as response:
|
||||
f1.set_result(None)
|
||||
async for event in response:
|
||||
i = i + 1
|
||||
if i == 1:
|
||||
self.assertEqual(event.type, EVENT_TYPE_CREATE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo')
|
||||
elif i == 2:
|
||||
self.assertEqual(event.type, EVENT_TYPE_MODIFY)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo1')
|
||||
elif i == 3:
|
||||
self.assertEqual(event.type, EVENT_TYPE_DELETE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
# delete event has no value
|
||||
# self.assertEqual(event.value, b'foo1')
|
||||
break
|
||||
|
||||
f2 = asyncio.get_event_loop().create_future()
|
||||
|
||||
async def watch_2():
|
||||
i = 0
|
||||
async for event in self.client.watch('/foo', prev_kv=True, create_event=True):
|
||||
if event is None:
|
||||
f2.set_result(None)
|
||||
continue
|
||||
|
||||
i = i + 1
|
||||
if i == 1:
|
||||
self.assertEqual(event.type, EVENT_TYPE_CREATE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo')
|
||||
elif i == 2:
|
||||
self.assertEqual(event.type, EVENT_TYPE_MODIFY)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo1')
|
||||
self.assertEqual(event.pre_value, b'foo')
|
||||
elif i == 3:
|
||||
self.assertEqual(event.type, EVENT_TYPE_DELETE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
# self.assertEqual(event.value, b'foo1')
|
||||
break
|
||||
|
||||
f3 = asyncio.get_event_loop().create_future()
|
||||
|
||||
async def watch_3():
|
||||
i = 0
|
||||
async for event in self.client.watch('/foo', prev_kv=True, noput=True, create_event=True):
|
||||
if event is None:
|
||||
f3.set_result(None)
|
||||
continue
|
||||
|
||||
i = i + 1
|
||||
if i == 1:
|
||||
self.assertEqual(event.type, EVENT_TYPE_DELETE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
# self.assertEqual(event.value, b'foo1')
|
||||
break
|
||||
|
||||
f4 = asyncio.get_event_loop().create_future()
|
||||
|
||||
async def watch_4():
|
||||
i = 0
|
||||
async for event in self.client.watch('/foo', prev_kv=True, nodelete=True, create_event=True):
|
||||
if event is None:
|
||||
f4.set_result(None)
|
||||
continue
|
||||
|
||||
i = i + 1
|
||||
if i == 1:
|
||||
self.assertEqual(event.type, EVENT_TYPE_CREATE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo')
|
||||
elif i == 2:
|
||||
self.assertEqual(event.type, EVENT_TYPE_MODIFY)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo1')
|
||||
self.assertEqual(event.pre_value, b'foo')
|
||||
break
|
||||
|
||||
w1 = asyncio.ensure_future(watch_1())
|
||||
w2 = asyncio.ensure_future(watch_2())
|
||||
w3 = asyncio.ensure_future(watch_3())
|
||||
w4 = asyncio.ensure_future(watch_4())
|
||||
|
||||
await asyncio.wait_for(asyncio.wait([f1, f2, f3, f4]), 2)
|
||||
|
||||
await self.client.put('/foo', 'foo')
|
||||
await self.client.put('/foo', 'foo1')
|
||||
await self.client.delete('/foo')
|
||||
|
||||
done, pending = await asyncio.wait([w1, w2, w3, w4], timeout=20)
|
||||
for t in done:
|
||||
t.result()
|
||||
|
||||
@asynctest
|
||||
async def test_watch_1(self):
|
||||
await self.common_watch1()
|
||||
|
||||
async def watch_reconnect(self):
|
||||
f1 = asyncio.get_event_loop().create_future()
|
||||
f2 = asyncio.get_event_loop().create_future()
|
||||
|
||||
async def watch_1():
|
||||
i = 0
|
||||
async with self.client.watch_scope('/foo') as response:
|
||||
f1.set_result(None)
|
||||
async for event in response:
|
||||
i = i + 1
|
||||
if i == 1:
|
||||
self.assertEqual(event.type, EVENT_TYPE_CREATE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo')
|
||||
f2.set_result(None)
|
||||
elif i == 2:
|
||||
self.assertEqual(event.type, EVENT_TYPE_MODIFY)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo1')
|
||||
elif i == 3:
|
||||
self.assertEqual(event.type, EVENT_TYPE_DELETE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
# delete event has no value
|
||||
# self.assertEqual(event.value, b'foo1')
|
||||
break
|
||||
|
||||
t1 = asyncio.ensure_future(watch_1())
|
||||
await f1
|
||||
await self.client.put('/foo', 'foo')
|
||||
await f2
|
||||
self.client.update_server_list(self.endpoints)
|
||||
await self.client.put('/foo', 'foo1')
|
||||
await self.client.delete('/foo')
|
||||
await t1
|
||||
|
||||
@asynctest
|
||||
async def test_watch_reconnect(self):
|
||||
await self.watch_reconnect()
|
||||
|
||||
async def watch_create_cancel(self):
|
||||
async def watch_1():
|
||||
async with self.client.watch_scope('/foo') as _:
|
||||
pass
|
||||
async def watch_2():
|
||||
async with self.client.watch_scope('/foo') as _:
|
||||
await asyncio.sleep(5)
|
||||
for _ in range(0, 5):
|
||||
watches = [asyncio.ensure_future(watch_1() if i % 2 else watch_2()) for i in range(0, 200)]
|
||||
await asyncio.sleep(1)
|
||||
for w in watches[::3]:
|
||||
w.cancel()
|
||||
self.client.update_server_list(self.endpoints)
|
||||
await asyncio.sleep(0.01)
|
||||
for w in watches[1::3]:
|
||||
w.cancel()
|
||||
await asyncio.sleep(0.3)
|
||||
for w in watches[2::3]:
|
||||
w.cancel()
|
||||
await asyncio.wait_for(asyncio.wait(watches), 3)
|
||||
results = await asyncio.gather(*watches, return_exceptions=True)
|
||||
print("Finished:", len([r for r in results if r is None]), "Cancelled:", len([r for r in results if r is not None]))
|
||||
self.assertIsNotNone(self.client._watch_task_running)
|
||||
await asyncio.sleep(3)
|
||||
self.assertIsNone(self.client._watch_task_running)
|
||||
|
||||
@asynctest
|
||||
async def test_watch_create_cancel(self):
|
||||
await self.watch_create_cancel()
|
||||
|
||||
async def batch_events(self):
|
||||
f1 = asyncio.get_event_loop().create_future()
|
||||
f2 = asyncio.get_event_loop().create_future()
|
||||
|
||||
def _check_event(e, criterias):
|
||||
if criterias[0]:
|
||||
self.assertEqual(e.type, criterias[0])
|
||||
if criterias[1]:
|
||||
self.assertEqual(e.key, criterias[1])
|
||||
if criterias[2]:
|
||||
self.assertEqual(e.value, criterias[2])
|
||||
|
||||
async def watch_1():
|
||||
asserts = [(EVENT_TYPE_CREATE, b'/foo/1', b'1'),
|
||||
(EVENT_TYPE_CREATE, b'/foo/2', b'2'),
|
||||
(EVENT_TYPE_MODIFY, b'/foo/1', b'2'),
|
||||
(EVENT_TYPE_MODIFY, b'/foo/2', b'3'),
|
||||
(EVENT_TYPE_DELETE, b'/foo/1', None),
|
||||
(EVENT_TYPE_DELETE, b'/foo/2', None)]
|
||||
async with self.client.watch_scope(range_prefix('/foo/')) as response:
|
||||
f1.set_result(None)
|
||||
async for e in response:
|
||||
_check_event(e, asserts.pop(0))
|
||||
if not asserts:
|
||||
break
|
||||
|
||||
async def watch_2():
|
||||
asserts = [((EVENT_TYPE_CREATE, b'/foo/1', b'1'),
|
||||
(EVENT_TYPE_CREATE, b'/foo/2', b'2'),),
|
||||
((EVENT_TYPE_MODIFY, b'/foo/1', b'2'),),
|
||||
((EVENT_TYPE_MODIFY, b'/foo/2', b'3'),),
|
||||
((EVENT_TYPE_DELETE, b'/foo/1', None),
|
||||
(EVENT_TYPE_DELETE, b'/foo/2', None))]
|
||||
async with self.client.watch_scope(range_prefix('/foo/'), batch_events=True) \
|
||||
as response:
|
||||
f2.set_result(None)
|
||||
async for es in response:
|
||||
batch = asserts.pop(0)
|
||||
self.assertEqual(len(es), len(batch))
|
||||
for e, a in zip(es, batch):
|
||||
_check_event(e, a)
|
||||
if not asserts:
|
||||
break
|
||||
|
||||
t1 = asyncio.ensure_future(watch_1())
|
||||
t2 = asyncio.ensure_future(watch_2())
|
||||
await asyncio.wait_for(asyncio.wait([f1, f2]), 2)
|
||||
self.assertTrue((await self.client.txn([], [self.client.put.txn('/foo/1', '1'),
|
||||
self.client.put.txn('/foo/2', '2')], []))[0])
|
||||
await self.client.put('/foo/1', '2')
|
||||
await self.client.put('/foo/2', '3')
|
||||
self.assertTrue((await self.client.txn([], [self.client.delete.txn('/foo/1'),
|
||||
self.client.delete.txn('/foo/2')], []))[0])
|
||||
await asyncio.gather(t1, t2)
|
||||
|
||||
@asynctest
|
||||
async def test_batch_events(self):
|
||||
await self.batch_events()
|
||||
|
||||
async def compact_revision(self):
|
||||
await self.client.put('/foo', '1')
|
||||
first_revision = self.client.last_response_info.revision
|
||||
await self.client.put('/foo', '2')
|
||||
await self.client.put('/foo', '3')
|
||||
await self.client.put('/foo', '4')
|
||||
await self.client.put('/foo', '5')
|
||||
compact_revision = self.client.last_response_info.revision
|
||||
await self.client.compact(compact_revision, True)
|
||||
|
||||
async def watch_1():
|
||||
async with self.client.watch_scope('/foo', start_revision=first_revision) as response:
|
||||
with self.assertRaises(CompactRevisonException) as cm:
|
||||
async for e in response:
|
||||
raise ValueError("Not raised")
|
||||
self.assertEqual(cm.exception.revision, compact_revision)
|
||||
|
||||
async def watch_2():
|
||||
async with self.client.watch_scope('/foo', ignore_compact=True, start_revision=first_revision) as responses:
|
||||
async for e in responses:
|
||||
self.assertEqual(e.type, EVENT_TYPE_MODIFY)
|
||||
self.assertEqual(e.key, b'/foo')
|
||||
self.assertEqual(e.value, b'5')
|
||||
self.assertEqual(e.revision, compact_revision)
|
||||
break
|
||||
|
||||
await watch_1()
|
||||
await watch_2()
|
||||
|
||||
@asynctest
|
||||
async def test_compact_revision(self):
|
||||
await self.compact_revision()
|
||||
|
||||
async def watch_exception(self):
|
||||
f1 = asyncio.get_event_loop().create_future()
|
||||
f2 = asyncio.get_event_loop().create_future()
|
||||
|
||||
async def watch_1():
|
||||
i = 0
|
||||
async with self.client.watch_scope('/foo') as response:
|
||||
f1.set_result(None)
|
||||
with self.assertRaises(WatchException):
|
||||
async for event in response:
|
||||
i = i + 1
|
||||
if i == 1:
|
||||
self.assertEqual(event.type, EVENT_TYPE_CREATE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo')
|
||||
f2.set_result(None)
|
||||
elif i == 2:
|
||||
raise ValueError("Not raised")
|
||||
|
||||
f3 = asyncio.get_event_loop().create_future()
|
||||
f4 = asyncio.get_event_loop().create_future()
|
||||
|
||||
async def watch_2():
|
||||
i = 0
|
||||
async with self.client.watch_scope('/foo', always_reconnect=True) as response:
|
||||
f3.set_result(None)
|
||||
async for event in response:
|
||||
i = i + 1
|
||||
if i == 1:
|
||||
self.assertEqual(event.type, EVENT_TYPE_CREATE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo')
|
||||
f4.set_result(None)
|
||||
elif i == 2:
|
||||
self.assertEqual(event.type, EVENT_TYPE_MODIFY)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
self.assertEqual(event.value, b'foo1')
|
||||
elif i == 3:
|
||||
self.assertEqual(event.type, EVENT_TYPE_DELETE)
|
||||
self.assertEqual(event.key, b'/foo')
|
||||
# delete event has no value
|
||||
# self.assertEqual(event.value, b'foo1')
|
||||
break
|
||||
|
||||
t1 = asyncio.ensure_future(watch_1())
|
||||
t2 = asyncio.ensure_future(watch_2())
|
||||
await f1
|
||||
await f3
|
||||
await self.client.put('/foo', 'foo')
|
||||
await f2
|
||||
await f4
|
||||
fake_endpoints = 'ipv4:///127.0.0.1:49999'
|
||||
self.client.update_server_list(fake_endpoints)
|
||||
await asyncio.sleep(2)
|
||||
self.client.update_server_list(self.endpoints)
|
||||
await self.client.put('/foo', 'foo1')
|
||||
await self.client.delete('/foo')
|
||||
await t1
|
||||
await t2
|
||||
|
||||
@asynctest
|
||||
async def test_watch_exception(self):
|
||||
await self.watch_exception()
|
||||
|
||||
async def _run_test_with_auth(self, test):
|
||||
default_client = self.client
|
||||
await switch_auth_on(default_client)
|
||||
root_client = client(endpoint=self.endpoints, username="root", password="root")
|
||||
await root_client.role_grant_permission(name='client', key_range=range_prefix('/foo'), permission=PER_RW)
|
||||
self.client = client(endpoint=self.endpoints, username="client", password="client")
|
||||
try:
|
||||
await test()
|
||||
finally:
|
||||
await switch_auth_off(
|
||||
root_client,
|
||||
default_client
|
||||
)
|
||||
await root_client.close()
|
||||
await self.client.close()
|
||||
self.client = default_client
|
||||
|
||||
@asynctest
|
||||
async def test_watch1_with_auth(self):
|
||||
await self._run_test_with_auth(self.common_watch1)
|
||||
|
||||
@asynctest
|
||||
async def test_watch_reconnect_with_auth(self):
|
||||
await self._run_test_with_auth(self.watch_reconnect)
|
||||
|
||||
@asynctest
|
||||
async def test_watch_create_cancel_with_auth(self):
|
||||
await self._run_test_with_auth(self.watch_create_cancel)
|
||||
|
||||
@asynctest
|
||||
async def test_batch_events_with_auth(self):
|
||||
await self._run_test_with_auth(self.batch_events)
|
||||
|
||||
@asynctest
|
||||
async def test_compact_revision_with_auth(self):
|
||||
await self._run_test_with_auth(self.compact_revision)
|
||||
|
||||
@asynctest
|
||||
async def test_watch_exception_with_auth(self):
|
||||
await self._run_test_with_auth(self.watch_exception)
|
||||
|
||||
@asynctest
|
||||
async def tearDown(self):
|
||||
await self.cleanUp()
|
||||
await self.client.close()
|
||||
|
||||
async def cleanUp(self):
|
||||
await self.client.delete(range_all())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
17
test/utils.py
Normal file
17
test/utils.py
Normal file
@@ -0,0 +1,17 @@
|
||||
async def switch_auth_on(client):
|
||||
await client.user_add(username="root", password="root")
|
||||
await client.role_add(name="root")
|
||||
await client.user_grant_role(username="root", role="root")
|
||||
|
||||
await client.user_add(username="client", password="client")
|
||||
await client.role_add(name="client")
|
||||
await client.user_grant_role(username="client", role="client")
|
||||
await client.auth_enable()
|
||||
|
||||
|
||||
async def switch_auth_off(root_client, unautheticated_client):
|
||||
await root_client.auth_disable()
|
||||
await unautheticated_client.user_delete("client")
|
||||
await unautheticated_client.user_delete("root")
|
||||
await unautheticated_client.role_delete("client")
|
||||
await unautheticated_client.role_delete("root")
|
||||
Reference in New Issue
Block a user