Skip to content

Commit 4123833

Browse files
committed
add tests for backend
1 parent 7378335 commit 4123833

File tree

2 files changed

+262
-36
lines changed

2 files changed

+262
-36
lines changed

contentcuration/contentcuration/tests/viewsets/test_recommendations.py

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
from automation.utils.appnexus import errors
22
from django.urls import reverse
3+
from le_utils.constants import content_kinds
34
from mock import patch
45

6+
from contentcuration.models import RecommendationsEvent
7+
from contentcuration.models import RecommendationsInteractionEvent
8+
from contentcuration.tests import testdata
59
from contentcuration.tests.base import StudioAPITestCase
610

711

@@ -125,3 +129,247 @@ def test_recommendation_generic_error(self, mock_load_recommendations):
125129
self.assertEqual(response.status_code, 500)
126130
self.assertEqual(response.content.decode(), error_message)
127131
mock_load_recommendations.assert_called_once()
132+
133+
134+
class RecommendationsEventViewSetTestCase(StudioAPITestCase):
135+
@property
136+
def recommendations_event_object(self):
137+
return {
138+
'context': {'model_version': 1, 'breadcrumbs': "#Title#->Random"},
139+
'contentnode_id': self.contentNode.id,
140+
'content_id': self.contentNode.content_id,
141+
'target_channel_id': self.channel.id,
142+
'user': self.user.id,
143+
'time_hidden': '2024-03-20T10:00:00Z',
144+
'content': [{'content_id': str(self.contentNode.content_id), 'node_id': str(self.contentNode.id), 'channel_id': str(self.channel.id), 'score': 4}]
145+
}
146+
147+
def setUp(self):
148+
super(RecommendationsEventViewSetTestCase, self).setUp()
149+
self.contentNode = testdata.node(
150+
{
151+
"kind_id": content_kinds.VIDEO,
152+
"title": "Recommended Video content",
153+
},
154+
)
155+
self.channel = testdata.channel()
156+
self.user = testdata.user()
157+
self.client.force_authenticate(user=self.user)
158+
159+
def test_create_recommendations_event(self):
160+
recommendations_event = self.recommendations_event_object
161+
response = self.client.post(
162+
reverse("recommendations-list"), recommendations_event, format="json",
163+
)
164+
self.assertEqual(response.status_code, 201, response.content)
165+
166+
def test_list_fails(self):
167+
response = self.client.get(reverse("recommendations-list"), format="json")
168+
self.assertEqual(response.status_code, 405, response.content)
169+
170+
def test_retrieve_fails(self):
171+
recommendations_event = RecommendationsEvent.objects.create(
172+
**{
173+
'context': {'model_version': 1, 'breadcrumbs': "#Title#->Random"},
174+
'contentnode_id': self.contentNode.id,
175+
'content_id': self.contentNode.content_id,
176+
'target_channel_id': self.channel.id,
177+
'time_hidden': '2024-03-20T10:00:00Z',
178+
'content': [{'content_id': str(self.contentNode.content_id),
179+
'node_id': str(self.contentNode.id),
180+
'channel_id': str(self.channel.id), 'score': 4}]
181+
},
182+
user=self.user,
183+
)
184+
response = self.client.get(reverse("recommendations-detail", kwargs={"pk": recommendations_event.id}), format="json")
185+
self.assertEqual(response.status_code, 405, response.content)
186+
187+
def test_update_recommendations_event(self):
188+
recommendations_event = RecommendationsEvent.objects.create(
189+
**{
190+
'context': {'model_version': 1, 'breadcrumbs': "#Title#->Random"},
191+
'contentnode_id': self.contentNode.id,
192+
'content_id': self.contentNode.content_id,
193+
'target_channel_id': self.channel.id,
194+
'time_hidden': '2024-03-20T10:00:00Z',
195+
'content': [{'content_id': str(self.contentNode.content_id),
196+
'node_id': str(self.contentNode.id),
197+
'channel_id': str(self.channel.id),
198+
'score': 4}]
199+
},
200+
user=self.user,
201+
)
202+
updated_data = self.recommendations_event_object
203+
updated_data['context'] = {'model_version': 2, 'breadcrumbs': "#Title#->Updated"}
204+
response = self.client.put(
205+
reverse("recommendations-detail", kwargs={"pk": recommendations_event.id}),
206+
updated_data,
207+
format="json"
208+
)
209+
self.assertEqual(response.status_code, 200, response.content)
210+
211+
def test_partial_update_recommendations_event(self):
212+
recommendations_event = RecommendationsEvent.objects.create(
213+
**{
214+
'context': {'model_version': 1, 'breadcrumbs': "#Title#->Random"},
215+
'contentnode_id': self.contentNode.id,
216+
'content_id': self.contentNode.content_id,
217+
'target_channel_id': self.channel.id,
218+
'time_hidden': '2024-03-20T10:00:00Z',
219+
'content': [{'content_id': str(self.contentNode.content_id),
220+
'node_id': str(self.contentNode.id),
221+
'channel_id': str(self.channel.id),
222+
'score': 4}]
223+
},
224+
user=self.user,
225+
)
226+
response = self.client.patch(
227+
reverse("recommendations-detail", kwargs={"pk": recommendations_event.id}),
228+
{'context': {'model_version': 2}},
229+
format="json"
230+
)
231+
self.assertEqual(response.status_code, 200, response.content)
232+
233+
def test_destroy_recommendations_event(self):
234+
recommendations_event = RecommendationsEvent.objects.create(
235+
**{
236+
'context': {'model_version': 1, 'breadcrumbs': "#Title#->Random"},
237+
'contentnode_id': self.contentNode.id,
238+
'content_id': self.contentNode.content_id,
239+
'target_channel_id': self.channel.id,
240+
'time_hidden': '2024-03-20T10:00:00Z',
241+
'content': [{'content_id': str(self.contentNode.content_id),
242+
'node_id': str(self.contentNode.id),
243+
'channel_id': str(self.channel.id),
244+
'score': 4}]
245+
},
246+
user=self.user,
247+
)
248+
response = self.client.delete(
249+
reverse("recommendations-detail", kwargs={"pk": recommendations_event.id}),
250+
format="json"
251+
)
252+
self.assertEqual(response.status_code, 204, response.content)
253+
254+
255+
class RecommendationsInteractionEventViewSetTestCase(StudioAPITestCase):
256+
@property
257+
def recommendations_interaction_object(self):
258+
return {
259+
'context': {'test_key': 'test_value'},
260+
'contentnode_id': self.interaction_node.id,
261+
'content_id': self.interaction_node.content_id,
262+
'feedback_type': 'IGNORED',
263+
'feedback_reason': '----',
264+
'recommendation_event_id': str(self.recommendation_event.id)
265+
}
266+
267+
def setUp(self):
268+
super(RecommendationsInteractionEventViewSetTestCase, self).setUp()
269+
self.channel = testdata.channel()
270+
self.user = testdata.user()
271+
self.client.force_authenticate(user=self.user)
272+
self.interaction_node = testdata.node(
273+
{
274+
"kind_id": content_kinds.VIDEO,
275+
"title": "Recommended Video content",
276+
},
277+
)
278+
self.node_where_import_is_initiated = testdata.node(
279+
{
280+
"kind_id": content_kinds.TOPIC,
281+
"title": "Node where content is imported",
282+
},
283+
)
284+
self.recommendation_event = RecommendationsEvent.objects.create(
285+
user=self.user,
286+
target_channel_id=self.channel.id,
287+
content_id=self.node_where_import_is_initiated.content_id,
288+
contentnode_id=self.node_where_import_is_initiated.id,
289+
context={'model_version': 1, 'breadcrumbs': "#Title#->Random"},
290+
time_hidden='2024-03-20T10:00:00Z',
291+
content=[{'content_id': str(self.interaction_node.content_id),
292+
'node_id': str(self.interaction_node.id),
293+
'channel_id': str(self.channel.id),
294+
'score': 4}]
295+
)
296+
297+
def test_create_recommendations_interaction(self):
298+
recommendations_interaction = self.recommendations_interaction_object
299+
response = self.client.post(
300+
reverse("recommendations-interaction-list"), recommendations_interaction, format="json",
301+
)
302+
self.assertEqual(response.status_code, 201, response.content)
303+
304+
def test_list_fails(self):
305+
response = self.client.get(reverse("recommendations-interaction-list"), format="json")
306+
self.assertEqual(response.status_code, 405, response.content)
307+
308+
def test_retrieve_fails(self):
309+
recommendations_interaction = RecommendationsInteractionEvent.objects.create(
310+
**{
311+
'context': {'test_key': 'test_value'},
312+
'contentnode_id': self.interaction_node.id,
313+
'content_id': self.interaction_node.content_id,
314+
'feedback_type': 'IGNORED',
315+
'feedback_reason': '----',
316+
'recommendation_event_id': self.recommendation_event.id
317+
}
318+
)
319+
response = self.client.get(reverse("recommendations-interaction-detail", kwargs={"pk": recommendations_interaction.id}), format="json")
320+
self.assertEqual(response.status_code, 405, response.content)
321+
322+
def test_update_recommendations_interaction(self):
323+
recommendations_interaction = RecommendationsInteractionEvent.objects.create(
324+
**{
325+
'context': {'test_key': 'test_value'},
326+
'contentnode_id': self.interaction_node.id,
327+
'content_id': self.interaction_node.content_id,
328+
'feedback_type': 'IGNORED',
329+
'feedback_reason': '----',
330+
'recommendation_event_id': self.recommendation_event.id
331+
}
332+
)
333+
updated_data = self.recommendations_interaction_object
334+
updated_data['feedback_type'] = 'PREVIEWED'
335+
response = self.client.put(
336+
reverse("recommendations-interaction-detail", kwargs={"pk": recommendations_interaction.id}),
337+
updated_data,
338+
format="json"
339+
)
340+
self.assertEqual(response.status_code, 200, response.content)
341+
342+
def test_partial_update_recommendations_interaction(self):
343+
recommendations_interaction = RecommendationsInteractionEvent.objects.create(
344+
**{
345+
'context': {'test_key': 'test_value'},
346+
'contentnode_id': self.interaction_node.id,
347+
'content_id': self.interaction_node.content_id,
348+
'feedback_type': 'IGNORED',
349+
'feedback_reason': '----',
350+
'recommendation_event_id': self.recommendation_event.id
351+
}
352+
)
353+
response = self.client.patch(
354+
reverse("recommendations-interaction-detail", kwargs={"pk": recommendations_interaction.id}),
355+
{'feedback_type': 'IMPORTED'},
356+
format="json"
357+
)
358+
self.assertEqual(response.status_code, 200, response.content)
359+
360+
def test_destroy_recommendations_interaction(self):
361+
recommendations_interaction = RecommendationsInteractionEvent.objects.create(
362+
**{
363+
'context': {'test_key': 'test_value'},
364+
'contentnode_id': self.interaction_node.id,
365+
'content_id': self.interaction_node.content_id,
366+
'feedback_type': 'IGNORED',
367+
'feedback_reason': '----',
368+
'recommendation_event_id': self.recommendation_event.id
369+
}
370+
)
371+
response = self.client.delete(
372+
reverse("recommendations-interaction-detail", kwargs={"pk": recommendations_interaction.id}),
373+
format="json"
374+
)
375+
self.assertEqual(response.status_code, 204, response.content)

contentcuration/contentcuration/viewsets/feedback.py

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,13 @@ class Meta:
5858
fields = BaseFeedbackSerializer.Meta.fields + BaseFeedbackInteractionEventSerializer.Meta.fields + ['recommendation_event_id']
5959

6060
def create(self, validated_data):
61-
return RecommendationsInteractionEvent(**validated_data)
61+
return RecommendationsInteractionEvent.objects.create(**validated_data)
6262

6363
def update(self, instance, validated_data):
64+
for attr, value in validated_data.items():
65+
setattr(instance, attr, value)
6466
instance.save()
67+
return instance
6568

6669

6770
class RecommendationsEventSerializer(BaseFeedbackSerializer, BaseFeedbackEventSerializer):
@@ -72,52 +75,27 @@ class Meta:
7275
fields = BaseFeedbackSerializer.Meta.fields + BaseFeedbackEventSerializer.Meta.fields + ['content', 'time_hidden']
7376

7477
def create(self, validated_data):
75-
return RecommendationsEvent(**validated_data)
78+
return RecommendationsEvent.objects.create(**validated_data)
7679

7780
def update(self, instance, validated_data):
81+
for attr, value in validated_data.items():
82+
setattr(instance, attr, value)
7883
instance.save()
84+
return instance
7985

8086

81-
class RecommendationsInteractionEventViewSet(
82-
viewsets.ViewSet,
83-
):
87+
class RecommendationsInteractionEventViewSet(viewsets.ModelViewSet):
88+
# TODO: decide export procedure
8489
queryset = RecommendationsInteractionEvent.objects.all()
8590
serializer_class = RecommendationsInteractionEventSerializer
91+
http_method_names = ['post', 'put', 'patch', 'delete']
8692

87-
# TODO: decide export mechansim to make use of gathered data
8893

89-
def create(self, request):
90-
pass
91-
92-
def update(self, request, pk=None):
93-
pass
94-
95-
def partial_update(self, request, pk=None):
96-
pass
97-
98-
def destroy(self, request, pk=None):
99-
pass
100-
101-
102-
class RecommendationsEventViewSet(
103-
viewsets.ViewSet
104-
):
94+
class RecommendationsEventViewSet(viewsets.ModelViewSet):
95+
# TODO: decide export procedure
10596
queryset = RecommendationsEvent.objects.all()
10697
serializer_class = RecommendationsEventSerializer
107-
108-
# TODO: decide export mechansim to make use of gathered data
109-
110-
def create(self, request):
111-
pass
112-
113-
def update(self, request, pk=None):
114-
pass
115-
116-
def partial_update(self, request, pk=None):
117-
pass
118-
119-
def destroy(self, request, pk=None):
120-
pass
98+
http_method_names = ['post', 'put', 'patch', 'delete']
12199

122100

123101
class FlagFeedbackEventViewSet(viewsets.ModelViewSet):

0 commit comments

Comments
 (0)