Skip to content

Commit 396cea1

Browse files
committed
Update tests for client and add todos for server
1 parent 3a98e79 commit 396cea1

File tree

4 files changed

+154
-24
lines changed

4 files changed

+154
-24
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
5+
# Virtual environment
6+
.env
7+
.venv

skpy/conn.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,12 +390,18 @@ def writeTokenToStr(self):
390390
Returns:
391391
str: A token string that can be used by :meth:`readTokenFromStr` to re-authenticate.
392392
"""
393+
# Use empty string and 0 expiry if registration token is not available yet
394+
regToken = self.tokens.get("reg", "")
395+
regExpiry = 0
396+
if "reg" in self.tokenExpiry:
397+
regExpiry = int(time.mktime(self.tokenExpiry["reg"].timetuple()))
398+
393399
return "\n".join([
394400
self.userId,
395401
self.tokens["skype"],
396402
str(int(time.mktime(self.tokenExpiry["skype"].timetuple()))),
397-
self.tokens["reg"],
398-
str(int(time.mktime(self.tokenExpiry["reg"].timetuple()))),
403+
regToken,
404+
str(regExpiry),
399405
self.msgsHost
400406
]) + "\n"
401407

test/API_FIXES_TODO.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# API Fixes TODO
2+
3+
## ✅ Working
4+
- Authentication ✅
5+
- User profile ✅
6+
- Services ✅
7+
8+
## ❌ Need Fixes
9+
10+
### 1. Contacts API
11+
- **Error**: User lookup returns `None`
12+
- **Fix**: Update contact search endpoints
13+
14+
### 2. Settings API
15+
- **Error**: DNS failure for `options.skype.com`
16+
- **Fix**: Find new settings endpoint URL
17+
18+
### 3. Translation API
19+
- **Error**: `404 from dev.microsofttranslator.com`
20+
- **Fix**: Update translator API endpoint
21+
22+
### 4. Subscriptions API
23+
- **Error**: `410 Gone` from event subscriptions
24+
- **Fix**: Investigate new subscription method
25+
26+
### 5. Chat API
27+
- **Error**: `401/404` from conversation endpoints
28+
- **Fix**: Check conversation ID format changes

test/client.py

Lines changed: 111 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,11 @@ def registerMocks(regTokenRedirect=False, guest=False):
9696
# SOAP login: exchange edge token.
9797
responses.add(responses.POST, SkypeConnection.API_EDGE, status=200, content_type="application/json",
9898
body=json.dumps({"skypetoken": Data.skypeToken, "expiresIn": 86400}))
99-
# Request registration token.
99+
# Registration tokens are now provided passively in response headers
100100
expiry = int(time.mktime((datetime.now() + timedelta(days=1)).timetuple()))
101101
msgsHost = Data.msgsHost if regTokenRedirect else SkypeConnection.API_MSGSHOST
102-
if regTokenRedirect:
103-
responses.add(responses.POST, "{0}/users/ME/endpoints".format(SkypeConnection.API_MSGSHOST), status=404,
104-
adding_headers={"Location": "{0}/users/ME/endpoints".format(Data.msgsHost)})
105-
responses.add(responses.POST, "{0}/users/ME/endpoints".format(msgsHost), status=200,
106-
adding_headers={"Set-RegistrationToken": "registrationToken={0}; expires={1}; endpointId={{{2}}}"
107-
.format(Data.regToken, expiry, Data.endpointId)})
102+
103+
# Note: No explicit endpoint registration requests - tokens come from normal API calls
108104
# Configure and retrieve endpoints.
109105
responses.add(responses.PUT, "{0}/users/ME/endpoints/%7B{1}%7D/presenceDocs/messagingService"
110106
.format(msgsHost, Data.endpointId), status=200)
@@ -118,6 +114,14 @@ def registerMocks(regTokenRedirect=False, guest=False):
118114
responses.add(responses.POST, "{0}/api/v2/conversation/".format(SkypeConnection.API_JOIN),
119115
status=200, content_type="application/json",
120116
json={"Long": Data.chatLongId, "Resource": Data.chatThreadId})
117+
# Guest login: get meeting information.
118+
responses.add(responses.GET, "{0}/meetings/{1}".format(SkypeConnection.API_JOIN_CREATE, Data.chatShortId),
119+
status=200, content_type="application/json",
120+
json={"threadId": Data.chatThreadId})
121+
# Guest login: join as guest user.
122+
responses.add(responses.POST, "{0}/threads/{1}/members".format(SkypeConnection.API_JOIN_CREATE, Data.chatThreadId),
123+
status=200, content_type="application/json",
124+
json={"skypetoken": Data.skypeToken})
121125
# Join a conversation as a guest.
122126
responses.add(responses.POST, "{0}/api/v1/users/guests".format(SkypeConnection.API_JOIN),
123127
status=200, content_type="application/json", json={"skypetoken": Data.skypeToken})
@@ -154,6 +158,8 @@ def registerMocks(regTokenRedirect=False, guest=False):
154158
chatFmt = (SkypeConnection.API_MSGSHOST, Data.chatThreadId)
155159
responses.add(responses.GET, "{0}/users/ME/conversations".format(SkypeConnection.API_MSGSHOST),
156160
status=200, content_type="application/json",
161+
adding_headers={"Set-RegistrationToken": "registrationToken={0}; expires={1}; endpointId={{{2}}}"
162+
.format(Data.regToken, expiry, Data.endpointId)},
157163
json={"conversations": [{"id": "8:{0}".format(Data.contactId),
158164
"lastMessage": {"clientmessageid": Data.msgId,
159165
"composetime": Data.msgTimeFmt,
@@ -295,7 +301,7 @@ def mockSkype():
295301
sk = Skype()
296302
sk.conn.userId = Data.userId
297303
sk.conn.tokens["skype"] = Data.skypeToken
298-
sk.conn.tokens["reg"] = "registrationToken={0}".format(Data.skypeToken)
304+
sk.conn.tokens["reg"] = Data.regToken
299305
sk.conn.tokenExpiry["skype"] = sk.conn.tokenExpiry["reg"] = Data.tokenExpiry
300306
return sk
301307

@@ -318,13 +324,16 @@ def testAuth(self):
318324
registerMocks()
319325
# Do the authentication.
320326
sk = Skype("fred.2", "password")
321-
# Tokens should be set.
327+
# Skype token should be set.
322328
self.assertEqual(sk.conn.tokens["skype"], Data.skypeToken)
323-
self.assertEqual(sk.conn.tokens["reg"], "registrationToken={0}".format(Data.regToken))
329+
# Registration token is now obtained passively, so it won't be present until an API call returns it
330+
# Make an API call that will trigger registration token extraction
331+
sk.chats.recent()
332+
# Now the registration token should be present
333+
self.assertEqual(sk.conn.tokens["reg"], Data.regToken)
324334
# Messenger host should be the default.
325335
self.assertEqual(sk.conn.msgsHost, SkypeConnection.API_MSGSHOST)
326-
# Main endpoint should exist.
327-
self.assertEqual(sk.conn.endpoints["main"].id, "{{{0}}}".format(Data.endpointId))
336+
# Note: With passive registration, endpoints are created as needed
328337
# Connected as our user, not a guest.
329338
self.assertTrue(sk.conn.connected)
330339
self.assertFalse(sk.conn.guest)
@@ -338,13 +347,16 @@ def testAuthRedirect(self):
338347
registerMocks(regTokenRedirect=True)
339348
# Do the authentication.
340349
sk = Skype("fred.2", "password")
341-
# Tokens should be set.
350+
# Skype token should be set.
342351
self.assertEqual(sk.conn.tokens["skype"], Data.skypeToken)
343-
self.assertEqual(sk.conn.tokens["reg"], "registrationToken={0}".format(Data.regToken))
344-
# Messenger host should be the alternative domain.
345-
self.assertEqual(sk.conn.msgsHost, Data.msgsHost)
346-
# Main endpoint should exist.
347-
self.assertEqual(sk.conn.endpoints["main"].id, "{{{0}}}".format(Data.endpointId))
352+
# Registration token is now obtained passively, so it won't be present until an API call returns it
353+
# Make an API call that will trigger registration token extraction
354+
sk.chats.recent()
355+
# Now the registration token should be present
356+
self.assertEqual(sk.conn.tokens["reg"], Data.regToken)
357+
# Messenger host should be the default (redirect handling changed).
358+
self.assertEqual(sk.conn.msgsHost, SkypeConnection.API_MSGSHOST)
359+
# Note: With passive registration, endpoints are created as needed
348360
# Connected as our user, not a guest.
349361
self.assertTrue(sk.conn.connected)
350362
self.assertFalse(sk.conn.guest)
@@ -361,13 +373,16 @@ def testGuestAuth(self):
361373
self.assertFalse(sk.conn.connected)
362374
# Do the authentication.
363375
sk.conn.guestLogin(Data.chatShortId, "Name")
364-
# Tokens should be set.
376+
# Skype token should be set.
365377
self.assertEqual(sk.conn.tokens["skype"], Data.skypeToken)
366-
self.assertEqual(sk.conn.tokens["reg"], "registrationToken={0}".format(Data.regToken))
378+
# Registration token is now obtained passively, so it won't be present until an API call returns it
379+
# Make an API call that will trigger registration token extraction
380+
sk.chats.recent()
381+
# Now the registration token should be present
382+
self.assertEqual(sk.conn.tokens["reg"], Data.regToken)
367383
# Messenger host should be the default.
368384
self.assertEqual(sk.conn.msgsHost, SkypeConnection.API_MSGSHOST)
369-
# Main endpoint should exist.
370-
self.assertEqual(sk.conn.endpoints["main"].id, "{{{0}}}".format(Data.endpointId))
385+
# Note: With passive registration, endpoints are created as needed
371386
# Connected as a guest user.
372387
self.assertTrue(sk.conn.connected)
373388
self.assertTrue(sk.conn.guest)
@@ -484,6 +499,80 @@ def testUtils(self):
484499
self.assertEqual(SkypeUtils.chatToId("{0}/conversations/{1}".format(Data.msgsHost, Data.chatP2PThreadId)),
485500
Data.chatP2PThreadId)
486501

502+
@responses.activate
503+
def testPassiveRegistrationTokenExtraction(self):
504+
"""
505+
Test that registration tokens are extracted from Set-RegistrationToken headers in any response.
506+
"""
507+
sk = mockSkype()
508+
# Clear existing registration token
509+
sk.conn.tokens.pop("reg", None)
510+
sk.conn.tokenExpiry.pop("reg", None)
511+
512+
expiry = int(time.mktime(Data.tokenExpiry.timetuple()))
513+
# Mock a request that returns a Set-RegistrationToken header
514+
responses.add(responses.GET, "{0}/users/ME/conversations".format(SkypeConnection.API_MSGSHOST),
515+
status=200, content_type="application/json",
516+
adding_headers={"Set-RegistrationToken": "registrationToken={0}; expires={1}"
517+
.format(Data.regToken, expiry)},
518+
json={"conversations": []})
519+
520+
# Make a request that should extract the token
521+
sk.chats.recent()
522+
523+
# Verify token was extracted
524+
self.assertEqual(sk.conn.tokens["reg"], Data.regToken)
525+
self.assertTrue("reg" in sk.conn.tokenExpiry)
526+
527+
@responses.activate
528+
def testRequiredHeaders(self):
529+
"""
530+
Test that required ms-ic3 headers are included in all requests.
531+
"""
532+
sk = mockSkype()
533+
534+
# Intercept the actual request to verify headers
535+
def request_callback(request):
536+
headers = request.headers
537+
self.assertIn('ms-ic3-additional-product', headers)
538+
self.assertEqual(headers['ms-ic3-additional-product'], 'Sfl')
539+
self.assertIn('ms-ic3-product', headers)
540+
self.assertEqual(headers['ms-ic3-product'], 'tfl')
541+
return (200, {}, json.dumps({"conversations": []}))
542+
543+
responses.add_callback(responses.GET, "{0}/users/ME/conversations".format(SkypeConnection.API_MSGSHOST),
544+
callback=request_callback, content_type="application/json")
545+
546+
sk.chats.recent()
547+
548+
@responses.activate
549+
def testPassiveTokenFromMultipleResponses(self):
550+
"""
551+
Test that registration tokens can be extracted from various API responses.
552+
"""
553+
sk = mockSkype()
554+
sk.conn.tokens.pop("reg", None)
555+
556+
expiry = int(time.mktime(Data.tokenExpiry.timetuple()))
557+
558+
# Mock a different API endpoint that could return registration tokens
559+
# Clear existing mocks for this endpoint first
560+
responses.reset()
561+
registerMocks() # Re-register all the standard mocks
562+
563+
# Add our specific test mock with registration token
564+
responses.add(responses.GET, "{0}/users/ME/conversations".format(SkypeConnection.API_MSGSHOST),
565+
status=200, content_type="application/json",
566+
adding_headers={"Set-RegistrationToken": "registrationToken={0}; expires={1}"
567+
.format(Data.regToken, expiry)},
568+
json={"conversations": []})
569+
570+
# Make a request that should extract the token
571+
sk.chats.recent()
572+
573+
# Verify token was extracted
574+
self.assertEqual(sk.conn.tokens["reg"], Data.regToken)
575+
487576

488577
if __name__ == "__main__":
489578
unittest.main()

0 commit comments

Comments
 (0)