pyrad 2.4__py3-none-any.whl → 2.5.0__py3-none-any.whl
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.
- docs/Makefile +20 -0
- docs/make.bat +36 -0
- docs/source/_static/logo.png +0 -0
- docs/source/api/client.rst +10 -0
- docs/source/api/dictionary.rst +10 -0
- docs/source/api/host.rst +7 -0
- docs/source/api/packet.rst +48 -0
- docs/source/api/proxy.rst +7 -0
- docs/source/api/server.rst +13 -0
- docs/source/conf.py +158 -0
- docs/source/index.rst +75 -0
- example/acct.py +41 -0
- example/auth.py +37 -0
- example/auth_async.py +164 -0
- example/client-coa.py +61 -0
- example/coa.py +40 -0
- example/dictionary +405 -0
- example/dictionary.freeradius +91 -0
- example/pyrad.log +0 -0
- example/server.py +68 -0
- example/server_async.py +117 -0
- example/status.py +26 -0
- pyrad/__init__.py +3 -3
- pyrad/client.py +14 -6
- pyrad/client_async.py +16 -13
- pyrad/dictfile.py +2 -5
- pyrad/dictionary.py +6 -7
- pyrad/host.py +1 -1
- pyrad/packet.py +145 -114
- pyrad/proxy.py +2 -2
- pyrad/server.py +3 -7
- pyrad/server_async.py +3 -4
- pyrad/tests/__init__.py +5 -0
- pyrad/tests/mock.py +145 -0
- pyrad/tests/test_bidict.py +56 -0
- pyrad/tests/test_client.py +183 -0
- pyrad/tests/test_dictionary.py +357 -0
- pyrad/tests/test_host.py +87 -0
- pyrad/tests/test_packet.py +679 -0
- pyrad/tests/test_proxy.py +96 -0
- pyrad/tests/test_server.py +323 -0
- pyrad/tests/test_tools.py +126 -0
- pyrad/tools.py +254 -158
- {pyrad-2.4.dist-info → pyrad-2.5.0.dist-info}/METADATA +44 -20
- pyrad-2.5.0.dist-info/RECORD +51 -0
- {pyrad-2.4.dist-info → pyrad-2.5.0.dist-info}/WHEEL +1 -1
- {pyrad-2.4.dist-info → pyrad-2.5.0.dist-info/licenses}/LICENSE.txt +1 -1
- pyrad-2.5.0.dist-info/top_level.txt +3 -0
- pyrad-2.4.dist-info/RECORD +0 -19
- pyrad-2.4.dist-info/top_level.txt +0 -1
- {pyrad-2.4.dist-info → pyrad-2.5.0.dist-info}/zip-safe +0 -0
pyrad/server_async.py
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
|
-
import traceback
|
|
8
7
|
|
|
9
8
|
from abc import abstractmethod, ABCMeta
|
|
10
9
|
from enum import Enum
|
|
@@ -81,7 +80,7 @@ class DatagramProtocolServer(asyncio.Protocol):
|
|
|
81
80
|
dict=self.server.dict,
|
|
82
81
|
packet=data)
|
|
83
82
|
if self.server.enable_pkt_verify:
|
|
84
|
-
if req.VerifyAuthRequest():
|
|
83
|
+
if not req.VerifyAuthRequest():
|
|
85
84
|
raise PacketError('Packet verification failed')
|
|
86
85
|
|
|
87
86
|
elif self.server_type == ServerType.Coa:
|
|
@@ -91,7 +90,7 @@ class DatagramProtocolServer(asyncio.Protocol):
|
|
|
91
90
|
dict=self.server.dict,
|
|
92
91
|
packet=data)
|
|
93
92
|
if self.server.enable_pkt_verify:
|
|
94
|
-
if req.VerifyCoARequest():
|
|
93
|
+
if not req.VerifyCoARequest():
|
|
95
94
|
raise PacketError('Packet verification failed')
|
|
96
95
|
|
|
97
96
|
elif self.server_type == ServerType.Acct:
|
|
@@ -102,7 +101,7 @@ class DatagramProtocolServer(asyncio.Protocol):
|
|
|
102
101
|
dict=self.server.dict,
|
|
103
102
|
packet=data)
|
|
104
103
|
if self.server.enable_pkt_verify:
|
|
105
|
-
if req.VerifyAcctRequest():
|
|
104
|
+
if not req.VerifyAcctRequest():
|
|
106
105
|
raise PacketError('Packet verification failed')
|
|
107
106
|
|
|
108
107
|
# Call request callback
|
pyrad/tests/__init__.py
ADDED
pyrad/tests/mock.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import fcntl
|
|
2
|
+
import os
|
|
3
|
+
from pyrad.packet import PacketError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MockPacket:
|
|
7
|
+
reply = object()
|
|
8
|
+
|
|
9
|
+
def __init__(self, code, verify=False, error=False):
|
|
10
|
+
self.code = code
|
|
11
|
+
self.data = {}
|
|
12
|
+
self.verify = verify
|
|
13
|
+
self.error = error
|
|
14
|
+
|
|
15
|
+
def CreateReply(self, packet=None):
|
|
16
|
+
if self.error:
|
|
17
|
+
raise PacketError
|
|
18
|
+
return self.reply
|
|
19
|
+
|
|
20
|
+
def VerifyReply(self, reply, rawreply):
|
|
21
|
+
return self.verify
|
|
22
|
+
|
|
23
|
+
def RequestPacket(self):
|
|
24
|
+
return "request packet"
|
|
25
|
+
|
|
26
|
+
def __contains__(self, key):
|
|
27
|
+
return key in self.data
|
|
28
|
+
has_key = __contains__
|
|
29
|
+
|
|
30
|
+
def __setitem__(self, key, value):
|
|
31
|
+
self.data[key] = [value]
|
|
32
|
+
|
|
33
|
+
def __getitem__(self, key):
|
|
34
|
+
return self.data[key]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MockSocket:
|
|
38
|
+
def __init__(self, domain, type, data=None):
|
|
39
|
+
self.domain = domain
|
|
40
|
+
self.type = type
|
|
41
|
+
self.closed = False
|
|
42
|
+
self.options = []
|
|
43
|
+
self.address = None
|
|
44
|
+
self.output = []
|
|
45
|
+
|
|
46
|
+
# Always initialize data so recv() never fails.
|
|
47
|
+
# If data is provided, use it; otherwise behave like "no data available".
|
|
48
|
+
self.data = data if data is not None else b""
|
|
49
|
+
|
|
50
|
+
if data is not None:
|
|
51
|
+
(self.read_end, self.write_end) = os.pipe()
|
|
52
|
+
fcntl.fcntl(self.write_end, fcntl.F_SETFL, os.O_NONBLOCK)
|
|
53
|
+
os.write(self.write_end, data)
|
|
54
|
+
else:
|
|
55
|
+
self.read_end = 1
|
|
56
|
+
self.write_end = None
|
|
57
|
+
|
|
58
|
+
def fileno(self):
|
|
59
|
+
return self.read_end
|
|
60
|
+
|
|
61
|
+
def bind(self, address):
|
|
62
|
+
self.address = address
|
|
63
|
+
|
|
64
|
+
def recv(self, buffer):
|
|
65
|
+
# Return up to `buffer` bytes; if empty, return b"" (no data).
|
|
66
|
+
return self.data[:buffer]
|
|
67
|
+
|
|
68
|
+
def sendto(self, data, target):
|
|
69
|
+
self.output.append((data, target))
|
|
70
|
+
|
|
71
|
+
def setsockopt(self, level, opt, value):
|
|
72
|
+
self.options.append((level, opt, value))
|
|
73
|
+
|
|
74
|
+
def close(self):
|
|
75
|
+
self.closed = True
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class MockFinished(Exception):
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class MockPoll:
|
|
83
|
+
results = []
|
|
84
|
+
|
|
85
|
+
def __init__(self):
|
|
86
|
+
self.registry = {}
|
|
87
|
+
|
|
88
|
+
def register(self, fd, options):
|
|
89
|
+
self.registry[fd] = options
|
|
90
|
+
|
|
91
|
+
def unregister(self, fd):
|
|
92
|
+
try:
|
|
93
|
+
del self.registry[fd]
|
|
94
|
+
except KeyError:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
def poll(self, timeout=None):
|
|
98
|
+
for result in self.results:
|
|
99
|
+
yield result
|
|
100
|
+
raise MockFinished
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def origkey(klass):
|
|
104
|
+
return "_originals_" + klass.__name__
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def MockClassMethod(klass, name, myfunc=None):
|
|
108
|
+
def func(self, *args, **kwargs):
|
|
109
|
+
if not hasattr(self, "called"):
|
|
110
|
+
self.called = []
|
|
111
|
+
self.called.append((name, args, kwargs))
|
|
112
|
+
|
|
113
|
+
key = origkey(klass)
|
|
114
|
+
if not hasattr(klass, key):
|
|
115
|
+
setattr(klass, key, {})
|
|
116
|
+
getattr(klass, key)[name] = getattr(klass, name)
|
|
117
|
+
if myfunc is None:
|
|
118
|
+
setattr(klass, name, func)
|
|
119
|
+
else:
|
|
120
|
+
setattr(klass, name, myfunc)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def UnmockClassMethods(klass):
|
|
124
|
+
key = origkey(klass)
|
|
125
|
+
if not hasattr(klass, key):
|
|
126
|
+
return
|
|
127
|
+
for (name, func) in getattr(klass, key).items():
|
|
128
|
+
setattr(klass, name, func)
|
|
129
|
+
|
|
130
|
+
delattr(klass, key)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class MockFd:
|
|
134
|
+
data = object()
|
|
135
|
+
source = object()
|
|
136
|
+
|
|
137
|
+
def __init__(self, fd=0):
|
|
138
|
+
self.fd = fd
|
|
139
|
+
|
|
140
|
+
def fileno(self):
|
|
141
|
+
return self.fd
|
|
142
|
+
|
|
143
|
+
def recvfrom(self, size):
|
|
144
|
+
self.size = size
|
|
145
|
+
return (self.data, self.source)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import operator
|
|
2
|
+
import unittest
|
|
3
|
+
from pyrad.bidict import BiDict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BiDictTests(unittest.TestCase):
|
|
7
|
+
def setUp(self):
|
|
8
|
+
self.bidict = BiDict()
|
|
9
|
+
|
|
10
|
+
def testStartEmpty(self):
|
|
11
|
+
self.assertEqual(len(self.bidict), 0)
|
|
12
|
+
self.assertEqual(len(self.bidict.forward), 0)
|
|
13
|
+
self.assertEqual(len(self.bidict.backward), 0)
|
|
14
|
+
|
|
15
|
+
def testLength(self):
|
|
16
|
+
self.assertEqual(len(self.bidict), 0)
|
|
17
|
+
self.bidict.Add("from", "to")
|
|
18
|
+
self.assertEqual(len(self.bidict), 1)
|
|
19
|
+
del self.bidict["from"]
|
|
20
|
+
self.assertEqual(len(self.bidict), 0)
|
|
21
|
+
|
|
22
|
+
def testDeletion(self):
|
|
23
|
+
self.assertRaises(KeyError, operator.delitem, self.bidict, "missing")
|
|
24
|
+
self.bidict.Add("missing", "present")
|
|
25
|
+
del self.bidict["missing"]
|
|
26
|
+
|
|
27
|
+
def testBackwardDeletion(self):
|
|
28
|
+
self.assertRaises(KeyError, operator.delitem, self.bidict, "missing")
|
|
29
|
+
self.bidict.Add("missing", "present")
|
|
30
|
+
del self.bidict["present"]
|
|
31
|
+
self.assertEqual(self.bidict.HasForward("missing"), False)
|
|
32
|
+
|
|
33
|
+
def testForwardAccess(self):
|
|
34
|
+
self.bidict.Add("shake", "vanilla")
|
|
35
|
+
self.bidict.Add("pie", "custard")
|
|
36
|
+
self.assertEqual(self.bidict.HasForward("shake"), True)
|
|
37
|
+
self.assertEqual(self.bidict.GetForward("shake"), "vanilla")
|
|
38
|
+
self.assertEqual(self.bidict.HasForward("pie"), True)
|
|
39
|
+
self.assertEqual(self.bidict.GetForward("pie"), "custard")
|
|
40
|
+
self.assertEqual(self.bidict.HasForward("missing"), False)
|
|
41
|
+
self.assertRaises(KeyError, self.bidict.GetForward, "missing")
|
|
42
|
+
|
|
43
|
+
def testBackwardAccess(self):
|
|
44
|
+
self.bidict.Add("shake", "vanilla")
|
|
45
|
+
self.bidict.Add("pie", "custard")
|
|
46
|
+
self.assertEqual(self.bidict.HasBackward("vanilla"), True)
|
|
47
|
+
self.assertEqual(self.bidict.GetBackward("vanilla"), "shake")
|
|
48
|
+
self.assertEqual(self.bidict.HasBackward("missing"), False)
|
|
49
|
+
self.assertRaises(KeyError, self.bidict.GetBackward, "missing")
|
|
50
|
+
|
|
51
|
+
def testItemAccessor(self):
|
|
52
|
+
self.bidict.Add("shake", "vanilla")
|
|
53
|
+
self.bidict.Add("pie", "custard")
|
|
54
|
+
self.assertRaises(KeyError, operator.getitem, self.bidict, "missing")
|
|
55
|
+
self.assertEqual(self.bidict["shake"], "vanilla")
|
|
56
|
+
self.assertEqual(self.bidict["pie"], "custard")
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import select
|
|
2
|
+
import socket
|
|
3
|
+
import unittest
|
|
4
|
+
from .mock import MockPacket
|
|
5
|
+
from .mock import MockPoll
|
|
6
|
+
from .mock import MockSocket
|
|
7
|
+
from pyrad.client import Client
|
|
8
|
+
from pyrad.client import Timeout
|
|
9
|
+
from pyrad.packet import AuthPacket
|
|
10
|
+
from pyrad.packet import AcctPacket
|
|
11
|
+
from pyrad.packet import AccessRequest
|
|
12
|
+
from pyrad.packet import AccountingRequest
|
|
13
|
+
|
|
14
|
+
BIND_IP = "127.0.0.1"
|
|
15
|
+
BIND_PORT = 53535
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ConstructionTests(unittest.TestCase):
|
|
19
|
+
def setUp(self):
|
|
20
|
+
self.server = object()
|
|
21
|
+
|
|
22
|
+
def testSimpleConstruction(self):
|
|
23
|
+
client = Client(self.server)
|
|
24
|
+
self.assertTrue(client.server is self.server)
|
|
25
|
+
self.assertEqual(client.authport, 1812)
|
|
26
|
+
self.assertEqual(client.acctport, 1813)
|
|
27
|
+
self.assertEqual(client.secret, b'')
|
|
28
|
+
self.assertEqual(client.retries, 3)
|
|
29
|
+
self.assertEqual(client.timeout, 5)
|
|
30
|
+
self.assertTrue(client.dict is None)
|
|
31
|
+
|
|
32
|
+
def testParameterOrder(self):
|
|
33
|
+
marker = object()
|
|
34
|
+
client = Client(self.server, 123, 456, 789, "secret", marker)
|
|
35
|
+
self.assertTrue(client.server is self.server)
|
|
36
|
+
self.assertEqual(client.authport, 123)
|
|
37
|
+
self.assertEqual(client.acctport, 456)
|
|
38
|
+
self.assertEqual(client.coaport, 789)
|
|
39
|
+
self.assertEqual(client.secret, "secret")
|
|
40
|
+
self.assertTrue(client.dict is marker)
|
|
41
|
+
|
|
42
|
+
def testNamedParameters(self):
|
|
43
|
+
marker = object()
|
|
44
|
+
client = Client(server=self.server, authport=123, acctport=456,
|
|
45
|
+
secret="secret", dict=marker)
|
|
46
|
+
self.assertTrue(client.server is self.server)
|
|
47
|
+
self.assertEqual(client.authport, 123)
|
|
48
|
+
self.assertEqual(client.acctport, 456)
|
|
49
|
+
self.assertEqual(client.secret, "secret")
|
|
50
|
+
self.assertTrue(client.dict is marker)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class SocketTests(unittest.TestCase):
|
|
54
|
+
def setUp(self):
|
|
55
|
+
self.server = object()
|
|
56
|
+
self.client = Client(self.server)
|
|
57
|
+
self.orgsocket = socket.socket
|
|
58
|
+
socket.socket = MockSocket
|
|
59
|
+
|
|
60
|
+
def tearDown(self):
|
|
61
|
+
socket.socket = self.orgsocket
|
|
62
|
+
|
|
63
|
+
def testReopen(self):
|
|
64
|
+
self.client._SocketOpen()
|
|
65
|
+
sock = self.client._socket
|
|
66
|
+
self.client._SocketOpen()
|
|
67
|
+
self.assertTrue(sock is self.client._socket)
|
|
68
|
+
|
|
69
|
+
def testBind(self):
|
|
70
|
+
self.client.bind((BIND_IP, BIND_PORT))
|
|
71
|
+
self.assertEqual(self.client._socket.address, (BIND_IP, BIND_PORT))
|
|
72
|
+
self.assertEqual(self.client._socket.options,
|
|
73
|
+
[(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)])
|
|
74
|
+
|
|
75
|
+
def testBindClosesSocket(self):
|
|
76
|
+
s = MockSocket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
77
|
+
self.client._socket = s
|
|
78
|
+
self.client._poll = MockPoll()
|
|
79
|
+
self.client.bind((BIND_IP, BIND_PORT))
|
|
80
|
+
self.assertEqual(s.closed, True)
|
|
81
|
+
|
|
82
|
+
def testSendPacket(self):
|
|
83
|
+
def MockSend(self, pkt, port):
|
|
84
|
+
self._mock_pkt = pkt
|
|
85
|
+
self._mock_port = port
|
|
86
|
+
|
|
87
|
+
_SendPacket = Client._SendPacket
|
|
88
|
+
Client._SendPacket = MockSend
|
|
89
|
+
|
|
90
|
+
self.client.SendPacket(AuthPacket())
|
|
91
|
+
self.assertEqual(self.client._mock_port, self.client.authport)
|
|
92
|
+
|
|
93
|
+
self.client.SendPacket(AcctPacket())
|
|
94
|
+
self.assertEqual(self.client._mock_port, self.client.acctport)
|
|
95
|
+
|
|
96
|
+
Client._SendPacket = _SendPacket
|
|
97
|
+
|
|
98
|
+
def testNoRetries(self):
|
|
99
|
+
self.client.retries = 0
|
|
100
|
+
self.assertRaises(Timeout, self.client._SendPacket, None, None)
|
|
101
|
+
|
|
102
|
+
def testSingleRetry(self):
|
|
103
|
+
self.client.retries = 1
|
|
104
|
+
self.client.timeout = 0
|
|
105
|
+
packet = MockPacket(AccessRequest)
|
|
106
|
+
self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
|
|
107
|
+
self.assertEqual(self.client._socket.output,
|
|
108
|
+
[("request packet", (self.server, 432))])
|
|
109
|
+
|
|
110
|
+
def testDoubleRetry(self):
|
|
111
|
+
self.client.retries = 2
|
|
112
|
+
self.client.timeout = 0
|
|
113
|
+
packet = MockPacket(AccessRequest)
|
|
114
|
+
self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
|
|
115
|
+
self.assertEqual(self.client._socket.output,
|
|
116
|
+
[("request packet", (self.server, 432)),
|
|
117
|
+
("request packet", (self.server, 432))])
|
|
118
|
+
|
|
119
|
+
def testAuthDelay(self):
|
|
120
|
+
self.client.retries = 2
|
|
121
|
+
self.client.timeout = 1
|
|
122
|
+
packet = MockPacket(AccessRequest)
|
|
123
|
+
self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
|
|
124
|
+
self.assertFalse("Acct-Delay-Time" in packet)
|
|
125
|
+
|
|
126
|
+
def testSingleAccountDelay(self):
|
|
127
|
+
self.client.retries = 2
|
|
128
|
+
self.client.timeout = 1
|
|
129
|
+
packet = MockPacket(AccountingRequest)
|
|
130
|
+
self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
|
|
131
|
+
self.assertEqual(packet["Acct-Delay-Time"], [1])
|
|
132
|
+
|
|
133
|
+
def testDoubleAccountDelay(self):
|
|
134
|
+
self.client.retries = 3
|
|
135
|
+
self.client.timeout = 1
|
|
136
|
+
packet = MockPacket(AccountingRequest)
|
|
137
|
+
self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
|
|
138
|
+
self.assertEqual(packet["Acct-Delay-Time"], [2])
|
|
139
|
+
|
|
140
|
+
def testIgnorePacketError(self):
|
|
141
|
+
self.client.retries = 1
|
|
142
|
+
self.client.timeout = 1
|
|
143
|
+
self.client._socket = MockSocket(1, 2, b'valid reply')
|
|
144
|
+
packet = MockPacket(AccountingRequest, verify=True, error=True)
|
|
145
|
+
self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
|
|
146
|
+
|
|
147
|
+
def testValidReply(self):
|
|
148
|
+
self.client.retries = 1
|
|
149
|
+
self.client.timeout = 1
|
|
150
|
+
self.client._socket = MockSocket(1, 2, b'valid reply')
|
|
151
|
+
self.client._poll = MockPoll()
|
|
152
|
+
MockPoll.results = [(1, select.POLLIN)]
|
|
153
|
+
packet = MockPacket(AccountingRequest, verify=True)
|
|
154
|
+
reply = self.client._SendPacket(packet, 432)
|
|
155
|
+
self.assertTrue(reply is packet.reply)
|
|
156
|
+
|
|
157
|
+
def testInvalidReply(self):
|
|
158
|
+
self.client.retries = 1
|
|
159
|
+
self.client.timeout = 1
|
|
160
|
+
self.client._socket = MockSocket(1, 2, b'invalid reply')
|
|
161
|
+
MockPoll.results = [(1, select.POLLIN)]
|
|
162
|
+
packet = MockPacket(AccountingRequest, verify=False)
|
|
163
|
+
self.assertRaises(Timeout, self.client._SendPacket, packet, 432)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class OtherTests(unittest.TestCase):
|
|
167
|
+
def setUp(self):
|
|
168
|
+
self.server = object()
|
|
169
|
+
self.client = Client(self.server, secret=b'zeer geheim')
|
|
170
|
+
|
|
171
|
+
def testCreateAuthPacket(self):
|
|
172
|
+
packet = self.client.CreateAuthPacket(id=15)
|
|
173
|
+
self.assertTrue(isinstance(packet, AuthPacket))
|
|
174
|
+
self.assertTrue(packet.dict is self.client.dict)
|
|
175
|
+
self.assertEqual(packet.id, 15)
|
|
176
|
+
self.assertEqual(packet.secret, b'zeer geheim')
|
|
177
|
+
|
|
178
|
+
def testCreateAcctPacket(self):
|
|
179
|
+
packet = self.client.CreateAcctPacket(id=15)
|
|
180
|
+
self.assertTrue(isinstance(packet, AcctPacket))
|
|
181
|
+
self.assertTrue(packet.dict is self.client.dict)
|
|
182
|
+
self.assertEqual(packet.id, 15)
|
|
183
|
+
self.assertEqual(packet.secret, b'zeer geheim')
|