Skip to content

Commit 29ac97f

Browse files
Henry Chanhenrylamchan
authored andcommitted
Audit trail get_events tests
1 parent 37fccb2 commit 29ac97f

File tree

6 files changed

+201
-46
lines changed

6 files changed

+201
-46
lines changed

tests/conftest.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,19 @@ def mock(*args, **kwargs):
4141

4242

4343
@pytest.fixture
44-
def capture_and_mock_requests(monkeypatch):
45-
def inner():
46-
captured_requests = []
44+
def capture_and_mock_request(monkeypatch):
45+
def inner(method, response_dict, status_code):
46+
request_args = []
47+
request_kwargs = {}
4748

48-
def capture(*args, **kwargs):
49-
captured_requests.append((args, kwargs))
50-
return MockResponse({}, 200)
49+
def capture_and_mock(*args, **kwargs):
50+
request_args.extend(args)
51+
request_kwargs.update(kwargs)
5152

52-
monkeypatch.setattr(requests, "get", capture)
53-
monkeypatch.setattr(requests, "post", capture)
53+
return MockResponse(response_dict, status_code)
5454

55-
return captured_requests
55+
monkeypatch.setattr(requests, method, capture_and_mock)
56+
57+
return (request_args, request_kwargs)
5658

5759
return inner

tests/test_audit_trail.py

Lines changed: 157 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,18 @@ def test_create_audit_trail_event_succeeds(self, mock_request_method):
2323
"actor_id": "user_12345",
2424
"target_name": "Ryota Yamasato",
2525
"target_id": "user_67890",
26-
"occurred_at": datetime.utcnow().isoformat(),
26+
"occurred_at": datetime.now().isoformat(),
2727
"metadata": {"a": "b"},
2828
}
2929
mock_response = Response()
3030
mock_response.status_code = 200
3131
mock_request_method("post", mock_response, 200)
32-
response = self.audit_trail.create_event(event)
33-
assert response.status_code == 200
32+
33+
result = self.audit_trail.create_event(event)
34+
assert result == True
3435

3536
def test_create_audit_trail_event_fails_with_long_metadata(self):
36-
with pytest.raises(Exception, match=r"Number of metadata keys exceeds .*"):
37+
with pytest.raises(ValueError, match=r"Number of metadata keys exceeds .*"):
3738
metadata = {str(num): num for num in range(51)}
3839
event = {
3940
"group": "Terrace House",
@@ -48,3 +49,155 @@ def test_create_audit_trail_event_fails_with_long_metadata(self):
4849
"metadata": metadata,
4950
}
5051
self.audit_trail.create_event(event)
52+
53+
def test_get_events_succeeds(self, mock_request_method):
54+
event = {
55+
"id": "evt_123",
56+
"group": "Terrace House",
57+
"location": "1.1.1.1",
58+
"latitude": None,
59+
"longitude": None,
60+
"action": {
61+
"id": "evt_action_123",
62+
"name": "house.created",
63+
"project_id": "project_123",
64+
},
65+
"type": "C",
66+
"actor_name": "Daiki Miyagi",
67+
"actor_id": "user_12345",
68+
"target_name": "Ryota Yamasato",
69+
"target_id": "user_67890",
70+
"occurred_at": datetime.now().isoformat(),
71+
"metadata": {"a": "b"},
72+
}
73+
74+
response = {
75+
"data": [event,],
76+
"listMetadata": {"before": None, "after": None,},
77+
}
78+
mock_request_method("get", response, 200)
79+
80+
events, before, after = self.audit_trail.get_events()
81+
assert events[0].to_dict() == event
82+
83+
def test_get_events_raises_valueerror_when_before_and_after_provided(self):
84+
with pytest.raises(ValueError):
85+
self.audit_trail.get_events(before="evt_123", after="evt_456")
86+
87+
def test_get_events_correctly_includes_occured_at_filter(
88+
self, capture_and_mock_request
89+
):
90+
event = {
91+
"id": "evt_123",
92+
"group": "Terrace House",
93+
"location": "1.1.1.1",
94+
"latitude": None,
95+
"longitude": None,
96+
"action": {
97+
"id": "evt_action_123",
98+
"name": "house.created",
99+
"project_id": "project_123",
100+
},
101+
"type": "C",
102+
"actor_name": "Daiki Miyagi",
103+
"actor_id": "user_12345",
104+
"target_name": "Ryota Yamasato",
105+
"target_id": "user_67890",
106+
"occurred_at": datetime.now().isoformat(),
107+
"metadata": {"a": "b"},
108+
}
109+
110+
response = {
111+
"data": [event,],
112+
"listMetadata": {"before": None, "after": None,},
113+
}
114+
request_args, request_kwargs = capture_and_mock_request("get", response, 200)
115+
116+
self.audit_trail.get_events(
117+
occurred_at=datetime.now(),
118+
occurred_at_gte=datetime.now(),
119+
occurred_at_gt=datetime.now(),
120+
occurred_at_lte=datetime.now,
121+
occurred_at_lt=datetime.now(),
122+
)
123+
124+
request_params = request_kwargs["params"]
125+
assert "occurred_at" in request_params
126+
assert "occurred_at_gte" not in request_params
127+
assert "occurred_at_gt" not in request_params
128+
assert "occurred_at_lte" not in request_params
129+
assert "occurred_at_lt" not in request_params
130+
131+
def test_get_events_correctly_includes_occurred_at_gte(
132+
self, capture_and_mock_request
133+
):
134+
event = {
135+
"id": "evt_123",
136+
"group": "Terrace House",
137+
"location": "1.1.1.1",
138+
"latitude": None,
139+
"longitude": None,
140+
"action": {
141+
"id": "evt_action_123",
142+
"name": "house.created",
143+
"project_id": "project_123",
144+
},
145+
"type": "C",
146+
"actor_name": "Daiki Miyagi",
147+
"actor_id": "user_12345",
148+
"target_name": "Ryota Yamasato",
149+
"target_id": "user_67890",
150+
"occurred_at": datetime.now().isoformat(),
151+
"metadata": {"a": "b"},
152+
}
153+
154+
response = {
155+
"data": [event,],
156+
"listMetadata": {"before": None, "after": None,},
157+
}
158+
request_args, request_kwargs = capture_and_mock_request("get", response, 200)
159+
160+
self.audit_trail.get_events(
161+
occurred_at_gte=datetime.now(), occurred_at_gt=datetime.now(),
162+
)
163+
164+
request_params = request_kwargs["params"]
165+
assert "occurred_at_gte" in request_params
166+
assert "occurred_at_gt" not in request_params
167+
168+
def test_get_events_correctly_includes_occured_at_lte(
169+
self, capture_and_mock_request
170+
):
171+
event = {
172+
"id": "evt_123",
173+
"group": "Terrace House",
174+
"location": "1.1.1.1",
175+
"latitude": None,
176+
"longitude": None,
177+
"action": {
178+
"id": "evt_action_123",
179+
"name": "house.created",
180+
"project_id": "project_123",
181+
},
182+
"type": "C",
183+
"actor_name": "Daiki Miyagi",
184+
"actor_id": "user_12345",
185+
"target_name": "Ryota Yamasato",
186+
"target_id": "user_67890",
187+
"occurred_at": datetime.now().isoformat(),
188+
"metadata": {"a": "b"},
189+
}
190+
191+
response = {
192+
"data": [event,],
193+
"listMetadata": {"before": None, "after": None,},
194+
}
195+
request_args, request_kwargs = capture_and_mock_request("get", response, 200)
196+
197+
self.audit_trail.get_events(
198+
occurred_at_lte=datetime.now, occurred_at_lt=datetime.now()
199+
)
200+
201+
request_params = request_kwargs["params"]
202+
assert "occurred_at_lte" in request_params
203+
assert "occurred_at_lt" not in request_params

tests/utils/test_requests.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,12 @@ def test_request_bad_body_raises_expected_exception_with_request_data(
7474
# This'll fail for sure here but... just using the nice error that'd come up
7575
assert ex.__class__ == ServerException
7676

77-
def test_request_includes_base_headers(self, capture_and_mock_requests):
78-
requests = capture_and_mock_requests()
77+
def test_request_includes_base_headers(self, capture_and_mock_request):
78+
request_args, request_kwargs = capture_and_mock_request("get", {}, 200)
7979

8080
RequestHelper().request("ok_place")
8181

82-
assert len(requests) == 1
83-
8482
base_headers = set(BASE_HEADERS.items())
85-
headers = set(requests[0][1]["headers"].items())
83+
headers = set(request_kwargs["headers"].items())
8684

8785
assert base_headers.issubset(headers)

workos/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55

66
api_key = os.getenv("WORKOS_API_KEY")
77
project_id = os.getenv("WORKOS_PROJECT_ID")
8-
base_api_url = "http://localhost:7000/"
8+
base_api_url = "https://api.workos.com/"

workos/audit_trail.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def create_event(self, event, idempotency_key=None):
5050
idempotency_key (str) - An idempotency key
5151
5252
Returns:
53-
dict: Response from WorkOS
53+
boolean: Returns True
5454
"""
5555
if len(event.get("metadata", {})) > METADATA_LIMIT:
5656
raise ValueError(
@@ -61,14 +61,16 @@ def create_event(self, event, idempotency_key=None):
6161
"idempotency-key": idempotency_key,
6262
}
6363

64-
return self.request_helper.request(
64+
self.request_helper.request(
6565
EVENTS_PATH,
6666
method=REQUEST_METHOD_POST,
6767
params=event,
6868
headers=headers,
6969
token=workos.api_key,
7070
)
7171

72+
return True
73+
7274
def get_events(
7375
self,
7476
before=None,
@@ -123,41 +125,41 @@ def get_events(
123125
"limit": limit,
124126
}
125127

126-
if group:
128+
if group is not None:
127129
params["group"] = list(group)
128130

129-
if action:
131+
if action is not None:
130132
params["action"] = list(action)
131133

132-
if action_type:
134+
if action_type is not None:
133135
params["action_type"] = list(action_type)
134136

135-
if actor_name:
137+
if actor_name is not None:
136138
params["actor_name"] = list(actor_name)
137139

138-
if actor_id:
140+
if actor_id is not None:
139141
params["actor_id"] = list(actor_id)
140142

141-
if target_name:
143+
if target_name is not None:
142144
params["target_name"] = list(target_name)
143145

144-
if target_id:
146+
if target_id is not None:
145147
params["target_id"] = list(target_id)
146148

147-
if occurred_at:
149+
if occurred_at is not None:
148150
params["occurred_at"] = occurred_at
149151
else:
150-
if occurred_at_gte:
152+
if occurred_at_gte is not None:
151153
params["occurred_at_gte"] = occurred_at_gte
152-
elif occurred_at_gt:
154+
elif occurred_at_gt is not None:
153155
params["occurred_at_gt"] = occurred_at_gt
154156

155-
if occurred_at_lte:
157+
if occurred_at_lte is not None:
156158
params["occurred_at_lte"] = occurred_at_lte
157-
elif occurred_at_lt:
159+
elif occurred_at_lt is not None:
158160
params["occurred_at_lt"] = occurred_at_lt
159161

160-
if search:
162+
if search is not None:
161163
params["search"] = search
162164

163165
response = self.request_helper.request(

workos/resources/base.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
class WorkOSBaseResource(object):
22
"""Representation of a WorkOS Resource as returned through the API.
33
4-
Attributes:
5-
OBJECT_FIELDS (list): List of fields a Resource is comprised of.
6-
"""
4+
Attributes:
5+
OBJECT_FIELDS (list): List of fields a Resource is comprised of.
6+
"""
77

88
OBJECT_FIELDS = []
99

1010
@classmethod
1111
def construct_from_response(cls, response):
1212
"""Returns an instance of WorkOSBaseResource.
1313
14-
Args:
15-
response (dict): Resource data from a WorkOS API response
14+
Args:
15+
response (dict): Resource data from a WorkOS API response
1616
17-
Returns:
18-
WorkOSBaseResource: Instance of a WorkOSBaseResource with OBJECT_FIELDS fields set
19-
"""
17+
Returns:
18+
WorkOSBaseResource: Instance of a WorkOSBaseResource with OBJECT_FIELDS fields set
19+
"""
2020
obj = cls()
2121
for field in cls.OBJECT_FIELDS:
2222
setattr(obj, field, response[field])
@@ -25,10 +25,10 @@ def construct_from_response(cls, response):
2525

2626
def to_dict(self):
2727
"""Returns a dict representation of the WorkOSBaseResource.
28-
29-
Returns:
30-
dict: A dict representation of the WorkOSBaseResource
31-
"""
28+
29+
Returns:
30+
dict: A dict representation of the WorkOSBaseResource
31+
"""
3232
obj_dict = {}
3333
for field in self.OBJECT_FIELDS:
3434
obj_dict[field] = getattr(self, field, None)

0 commit comments

Comments
 (0)