Skip to content

Commit 5584b8c

Browse files
committed
NO-ISSUE Add test for verifyChannelTokenByJWT and getsAllValidChannelAccessTokenKeyIds
1 parent 8ef2ad5 commit 5584b8c

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
"""
16+
Regression tests verifying that query parameter names are sent as snake_case,
17+
not camelCase.
18+
"""
19+
20+
import unittest
21+
from urllib.parse import parse_qs
22+
23+
from pytest_httpserver.httpserver import HTTPServer
24+
25+
from linebot.v3.oauth import (
26+
ApiClient,
27+
Configuration,
28+
ChannelAccessToken,
29+
)
30+
31+
32+
class TestQueryParamNamesAreSnakeCase(unittest.TestCase):
33+
"""
34+
Verify that GET endpoints send snake_case query keys to the LINE API,
35+
not camelCase keys produced by the OpenAPI generator's paramName conversion.
36+
"""
37+
38+
def test_verify_channel_token_by_jwt_uses_snake_case_key(self):
39+
"""
40+
verify_channel_token_by_jwt() must send `access_token=...` in the query
41+
string, NOT `accessToken=...`.
42+
43+
Endpoint: GET /oauth2/v2.1/verify
44+
"""
45+
dummy_access_token = "dummy_access_token_value"
46+
47+
expected_response = {
48+
"client_id": "1234567890",
49+
"expires_in": 3599,
50+
"scope": "profile",
51+
}
52+
53+
with HTTPServer() as httpserver:
54+
httpserver.expect_request(
55+
uri="/oauth2/v2.1/verify",
56+
method="GET",
57+
).respond_with_json(expected_response, status=200)
58+
59+
configuration = Configuration(
60+
host=httpserver.url_for("/")
61+
)
62+
63+
with ApiClient(configuration) as api_client:
64+
api = ChannelAccessToken(api_client)
65+
api.verify_channel_token_by_jwt(access_token=dummy_access_token)
66+
67+
self.assertEqual(len(httpserver.log), 1)
68+
req, _ = httpserver.log[0]
69+
70+
query_string = req.query_string.decode("utf-8")
71+
parsed = parse_qs(query_string)
72+
73+
# snake_case key must be present
74+
self.assertIn(
75+
"access_token", parsed,
76+
"Query string must contain 'access_token' (snake_case), "
77+
f"but got: {query_string!r}"
78+
)
79+
self.assertEqual(parsed["access_token"], [dummy_access_token])
80+
81+
# camelCase key must NOT be present
82+
self.assertNotIn(
83+
"accessToken", parsed,
84+
"Query string must NOT contain 'accessToken' (camelCase), "
85+
f"but got: {query_string!r}"
86+
)
87+
88+
def test_gets_all_valid_channel_access_token_key_ids_uses_snake_case_keys(self):
89+
"""
90+
gets_all_valid_channel_access_token_key_ids() must send
91+
`client_assertion_type=...` and `client_assertion=...` in the query
92+
string, NOT `clientAssertionType=...` / `clientAssertion=...`.
93+
94+
Endpoint: GET /oauth2/v2.1/tokens/kid
95+
"""
96+
dummy_client_assertion_type = (
97+
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
98+
)
99+
dummy_client_assertion = "eyJhbGciOiJSUzI1NiJ9.dummy.signature"
100+
101+
expected_response = {
102+
"kids": ["key_id_1", "key_id_2"],
103+
}
104+
105+
with HTTPServer() as httpserver:
106+
httpserver.expect_request(
107+
uri="/oauth2/v2.1/tokens/kid",
108+
method="GET",
109+
).respond_with_json(expected_response, status=200)
110+
111+
configuration = Configuration(
112+
host=httpserver.url_for("/")
113+
)
114+
115+
with ApiClient(configuration) as api_client:
116+
api = ChannelAccessToken(api_client)
117+
api.gets_all_valid_channel_access_token_key_ids(
118+
client_assertion_type=dummy_client_assertion_type,
119+
client_assertion=dummy_client_assertion,
120+
)
121+
122+
self.assertEqual(len(httpserver.log), 1)
123+
req, _ = httpserver.log[0]
124+
125+
query_string = req.query_string.decode("utf-8")
126+
parsed = parse_qs(query_string)
127+
128+
# snake_case keys must be present
129+
self.assertIn(
130+
"client_assertion_type", parsed,
131+
"Query string must contain 'client_assertion_type' (snake_case), "
132+
f"but got: {query_string!r}"
133+
)
134+
self.assertEqual(
135+
parsed["client_assertion_type"], [dummy_client_assertion_type]
136+
)
137+
138+
self.assertIn(
139+
"client_assertion", parsed,
140+
"Query string must contain 'client_assertion' (snake_case), "
141+
f"but got: {query_string!r}"
142+
)
143+
self.assertEqual(
144+
parsed["client_assertion"], [dummy_client_assertion]
145+
)
146+
147+
# camelCase keys must NOT be present
148+
self.assertNotIn(
149+
"clientAssertionType", parsed,
150+
"Query string must NOT contain 'clientAssertionType' (camelCase), "
151+
f"but got: {query_string!r}"
152+
)
153+
self.assertNotIn(
154+
"clientAssertion", parsed,
155+
"Query string must NOT contain 'clientAssertion' (camelCase), "
156+
f"but got: {query_string!r}"
157+
)
158+
159+
160+
if __name__ == "__main__":
161+
unittest.main()

0 commit comments

Comments
 (0)