Blacked the code

This commit is contained in:
Eliot Berriot 2018-06-09 15:36:16 +02:00
commit 62ca3bd736
No known key found for this signature in database
GPG key ID: DD6965E2476E5C27
279 changed files with 8890 additions and 9556 deletions

View file

@ -9,151 +9,125 @@ from funkwhale_api.radios import serializers
def test_can_list_config_options(logged_in_client):
url = reverse('api:v1:radios:radios-filters')
url = reverse("api:v1:radios:radios-filters")
response = logged_in_client.get(url)
assert response.status_code == 200
payload = json.loads(response.content.decode('utf-8'))
payload = json.loads(response.content.decode("utf-8"))
expected = [f for f in filters.registry.values() if f.expose_in_api]
assert len(payload) == len(expected)
def test_can_validate_config(logged_in_client, factories):
artist1 = factories['music.Artist']()
artist2 = factories['music.Artist']()
factories['music.Track'].create_batch(3, artist=artist1)
factories['music.Track'].create_batch(3, artist=artist2)
candidates = artist1.tracks.order_by('pk')
f = {
'filters': [
{'type': 'artist', 'ids': [artist1.pk]}
]
}
url = reverse('api:v1:radios:radios-validate')
artist1 = factories["music.Artist"]()
artist2 = factories["music.Artist"]()
factories["music.Track"].create_batch(3, artist=artist1)
factories["music.Track"].create_batch(3, artist=artist2)
candidates = artist1.tracks.order_by("pk")
f = {"filters": [{"type": "artist", "ids": [artist1.pk]}]}
url = reverse("api:v1:radios:radios-validate")
response = logged_in_client.post(
url,
json.dumps(f),
content_type="application/json")
url, json.dumps(f), content_type="application/json"
)
assert response.status_code == 200
payload = json.loads(response.content.decode('utf-8'))
payload = json.loads(response.content.decode("utf-8"))
expected = {
'count': candidates.count(),
'sample': TrackSerializer(candidates, many=True).data
"count": candidates.count(),
"sample": TrackSerializer(candidates, many=True).data,
}
assert payload['filters'][0]['candidates'] == expected
assert payload['filters'][0]['errors'] == []
assert payload["filters"][0]["candidates"] == expected
assert payload["filters"][0]["errors"] == []
def test_can_validate_config_with_wrong_config(logged_in_client, factories):
f = {
'filters': [
{'type': 'artist', 'ids': [999]}
]
}
url = reverse('api:v1:radios:radios-validate')
f = {"filters": [{"type": "artist", "ids": [999]}]}
url = reverse("api:v1:radios:radios-validate")
response = logged_in_client.post(
url,
json.dumps(f),
content_type="application/json")
url, json.dumps(f), content_type="application/json"
)
assert response.status_code == 200
payload = json.loads(response.content.decode('utf-8'))
payload = json.loads(response.content.decode("utf-8"))
expected = {
'count': None,
'sample': None
}
assert payload['filters'][0]['candidates'] == expected
assert len(payload['filters'][0]['errors']) == 1
expected = {"count": None, "sample": None}
assert payload["filters"][0]["candidates"] == expected
assert len(payload["filters"][0]["errors"]) == 1
def test_saving_radio_sets_user(logged_in_client, factories):
artist = factories['music.Artist']()
f = {
'name': 'Test',
'config': [
{'type': 'artist', 'ids': [artist.pk]}
]
}
url = reverse('api:v1:radios:radios-list')
artist = factories["music.Artist"]()
f = {"name": "Test", "config": [{"type": "artist", "ids": [artist.pk]}]}
url = reverse("api:v1:radios:radios-list")
response = logged_in_client.post(
url,
json.dumps(f),
content_type="application/json")
url, json.dumps(f), content_type="application/json"
)
assert response.status_code == 201
radio = logged_in_client.user.radios.latest('id')
assert radio.name == 'Test'
radio = logged_in_client.user.radios.latest("id")
assert radio.name == "Test"
assert radio.user == logged_in_client.user
def test_user_can_detail_his_radio(logged_in_client, factories):
radio = factories['radios.Radio'](user=logged_in_client.user)
url = reverse('api:v1:radios:radios-detail', kwargs={'pk': radio.pk})
radio = factories["radios.Radio"](user=logged_in_client.user)
url = reverse("api:v1:radios:radios-detail", kwargs={"pk": radio.pk})
response = logged_in_client.get(url)
assert response.status_code == 200
def test_user_can_detail_public_radio(logged_in_client, factories):
radio = factories['radios.Radio'](is_public=True)
url = reverse('api:v1:radios:radios-detail', kwargs={'pk': radio.pk})
radio = factories["radios.Radio"](is_public=True)
url = reverse("api:v1:radios:radios-detail", kwargs={"pk": radio.pk})
response = logged_in_client.get(url)
assert response.status_code == 200
def test_user_cannot_detail_someone_else_radio(logged_in_client, factories):
radio = factories['radios.Radio'](is_public=False)
url = reverse('api:v1:radios:radios-detail', kwargs={'pk': radio.pk})
radio = factories["radios.Radio"](is_public=False)
url = reverse("api:v1:radios:radios-detail", kwargs={"pk": radio.pk})
response = logged_in_client.get(url)
assert response.status_code == 404
def test_user_can_edit_his_radio(logged_in_client, factories):
radio = factories['radios.Radio'](user=logged_in_client.user)
url = reverse('api:v1:radios:radios-detail', kwargs={'pk': radio.pk})
radio = factories["radios.Radio"](user=logged_in_client.user)
url = reverse("api:v1:radios:radios-detail", kwargs={"pk": radio.pk})
response = logged_in_client.put(
url,
json.dumps({'name': 'new', 'config': []}),
content_type="application/json")
url, json.dumps({"name": "new", "config": []}), content_type="application/json"
)
radio.refresh_from_db()
assert response.status_code == 200
assert radio.name == 'new'
assert radio.name == "new"
def test_user_cannot_edit_someone_else_radio(logged_in_client, factories):
radio = factories['radios.Radio']()
url = reverse('api:v1:radios:radios-detail', kwargs={'pk': radio.pk})
radio = factories["radios.Radio"]()
url = reverse("api:v1:radios:radios-detail", kwargs={"pk": radio.pk})
response = logged_in_client.put(
url,
json.dumps({'name': 'new', 'config': []}),
content_type="application/json")
url, json.dumps({"name": "new", "config": []}), content_type="application/json"
)
assert response.status_code == 404
def test_clean_config_is_called_on_serializer_save(mocker, factories):
user = factories['users.User']()
artist = factories['music.Artist']()
data= {
'name': 'Test',
'config': [
{'type': 'artist', 'ids': [artist.pk]}
]
}
spied = mocker.spy(filters.registry['artist'], 'clean_config')
user = factories["users.User"]()
artist = factories["music.Artist"]()
data = {"name": "Test", "config": [{"type": "artist", "ids": [artist.pk]}]}
spied = mocker.spy(filters.registry["artist"], "clean_config")
serializer = serializers.RadioSerializer(data=data)
assert serializer.is_valid()
instance = serializer.save(user=user)
spied.assert_called_once_with(data['config'][0])
assert instance.config[0]['names'] == [artist.name]
spied.assert_called_once_with(data["config"][0])
assert instance.config[0]["names"] == [artist.name]

View file

@ -8,154 +8,147 @@ from funkwhale_api.radios import filters
@filters.registry.register
class NoopFilter(filters.RadioFilter):
code = 'noop'
code = "noop"
def get_query(self, candidates, **kwargs):
return
def test_most_simple_radio_does_not_filter_anything(factories):
tracks = factories['music.Track'].create_batch(3)
radio = factories['radios.Radio'](config=[{'type': 'noop'}])
tracks = factories["music.Track"].create_batch(3)
radio = factories["radios.Radio"](config=[{"type": "noop"}])
assert radio.version == 0
assert radio.get_candidates().count() == 3
def test_filter_can_use_custom_queryset(factories):
tracks = factories['music.Track'].create_batch(3)
tracks = factories["music.Track"].create_batch(3)
candidates = Track.objects.filter(pk=tracks[0].pk)
qs = filters.run([{'type': 'noop'}], candidates=candidates)
qs = filters.run([{"type": "noop"}], candidates=candidates)
assert qs.count() == 1
assert qs.first() == tracks[0]
def test_filter_on_tag(factories):
tracks = factories['music.Track'].create_batch(3, tags=['metal'])
factories['music.Track'].create_batch(3, tags=['pop'])
tracks = factories["music.Track"].create_batch(3, tags=["metal"])
factories["music.Track"].create_batch(3, tags=["pop"])
expected = tracks
f = [
{'type': 'tag', 'names': ['metal']}
]
f = [{"type": "tag", "names": ["metal"]}]
candidates = filters.run(f)
assert list(candidates.order_by('pk')) == expected
assert list(candidates.order_by("pk")) == expected
def test_filter_on_artist(factories):
artist1 = factories['music.Artist']()
artist2 = factories['music.Artist']()
factories['music.Track'].create_batch(3, artist=artist1)
factories['music.Track'].create_batch(3, artist=artist2)
expected = list(artist1.tracks.order_by('pk'))
f = [
{'type': 'artist', 'ids': [artist1.pk]}
]
artist1 = factories["music.Artist"]()
artist2 = factories["music.Artist"]()
factories["music.Track"].create_batch(3, artist=artist1)
factories["music.Track"].create_batch(3, artist=artist2)
expected = list(artist1.tracks.order_by("pk"))
f = [{"type": "artist", "ids": [artist1.pk]}]
candidates = filters.run(f)
assert list(candidates.order_by('pk')) == expected
assert list(candidates.order_by("pk")) == expected
def test_can_combine_with_or(factories):
artist1 = factories['music.Artist']()
artist2 = factories['music.Artist']()
artist3 = factories['music.Artist']()
factories['music.Track'].create_batch(3, artist=artist1)
factories['music.Track'].create_batch(3, artist=artist2)
factories['music.Track'].create_batch(3, artist=artist3)
expected = Track.objects.exclude(artist=artist3).order_by('pk')
artist1 = factories["music.Artist"]()
artist2 = factories["music.Artist"]()
artist3 = factories["music.Artist"]()
factories["music.Track"].create_batch(3, artist=artist1)
factories["music.Track"].create_batch(3, artist=artist2)
factories["music.Track"].create_batch(3, artist=artist3)
expected = Track.objects.exclude(artist=artist3).order_by("pk")
f = [
{'type': 'artist', 'ids': [artist1.pk]},
{'type': 'artist', 'ids': [artist2.pk], 'operator': 'or'},
{"type": "artist", "ids": [artist1.pk]},
{"type": "artist", "ids": [artist2.pk], "operator": "or"},
]
candidates = filters.run(f)
assert list(candidates.order_by('pk')) == list(expected)
assert list(candidates.order_by("pk")) == list(expected)
def test_can_combine_with_and(factories):
artist1 = factories['music.Artist']()
artist2 = factories['music.Artist']()
metal_tracks = factories['music.Track'].create_batch(
2, artist=artist1, tags=['metal'])
factories['music.Track'].create_batch(2, artist=artist1, tags=['pop'])
factories['music.Track'].create_batch(3, artist=artist2)
artist1 = factories["music.Artist"]()
artist2 = factories["music.Artist"]()
metal_tracks = factories["music.Track"].create_batch(
2, artist=artist1, tags=["metal"]
)
factories["music.Track"].create_batch(2, artist=artist1, tags=["pop"])
factories["music.Track"].create_batch(3, artist=artist2)
expected = metal_tracks
f = [
{'type': 'artist', 'ids': [artist1.pk]},
{'type': 'tag', 'names': ['metal'], 'operator': 'and'},
{"type": "artist", "ids": [artist1.pk]},
{"type": "tag", "names": ["metal"], "operator": "and"},
]
candidates = filters.run(f)
assert list(candidates.order_by('pk')) == list(expected)
assert list(candidates.order_by("pk")) == list(expected)
def test_can_negate(factories):
artist1 = factories['music.Artist']()
artist2 = factories['music.Artist']()
factories['music.Track'].create_batch(3, artist=artist1)
factories['music.Track'].create_batch(3, artist=artist2)
expected = artist2.tracks.order_by('pk')
f = [
{'type': 'artist', 'ids': [artist1.pk], 'not': True},
]
artist1 = factories["music.Artist"]()
artist2 = factories["music.Artist"]()
factories["music.Track"].create_batch(3, artist=artist1)
factories["music.Track"].create_batch(3, artist=artist2)
expected = artist2.tracks.order_by("pk")
f = [{"type": "artist", "ids": [artist1.pk], "not": True}]
candidates = filters.run(f)
assert list(candidates.order_by('pk')) == list(expected)
assert list(candidates.order_by("pk")) == list(expected)
def test_can_group(factories):
artist1 = factories['music.Artist']()
artist2 = factories['music.Artist']()
factories['music.Track'].create_batch(2, artist=artist1)
t1 = factories['music.Track'].create_batch(
2, artist=artist1, tags=['metal'])
factories['music.Track'].create_batch(2, artist=artist2)
t2 = factories['music.Track'].create_batch(
2, artist=artist2, tags=['metal'])
factories['music.Track'].create_batch(2, tags=['metal'])
artist1 = factories["music.Artist"]()
artist2 = factories["music.Artist"]()
factories["music.Track"].create_batch(2, artist=artist1)
t1 = factories["music.Track"].create_batch(2, artist=artist1, tags=["metal"])
factories["music.Track"].create_batch(2, artist=artist2)
t2 = factories["music.Track"].create_batch(2, artist=artist2, tags=["metal"])
factories["music.Track"].create_batch(2, tags=["metal"])
expected = t1 + t2
f = [
{'type': 'tag', 'names': ['metal']},
{'type': 'group', 'operator': 'and', 'filters': [
{'type': 'artist', 'ids': [artist1.pk], 'operator': 'or'},
{'type': 'artist', 'ids': [artist2.pk], 'operator': 'or'},
]}
{"type": "tag", "names": ["metal"]},
{
"type": "group",
"operator": "and",
"filters": [
{"type": "artist", "ids": [artist1.pk], "operator": "or"},
{"type": "artist", "ids": [artist2.pk], "operator": "or"},
],
},
]
candidates = filters.run(f)
assert list(candidates.order_by('pk')) == list(expected)
assert list(candidates.order_by("pk")) == list(expected)
def test_artist_filter_clean_config(factories):
artist1 = factories['music.Artist']()
artist2 = factories['music.Artist']()
artist1 = factories["music.Artist"]()
artist2 = factories["music.Artist"]()
config = filters.clean_config(
{'type': 'artist', 'ids': [artist2.pk, artist1.pk]})
config = filters.clean_config({"type": "artist", "ids": [artist2.pk, artist1.pk]})
expected = {
'type': 'artist',
'ids': [artist1.pk, artist2.pk],
'names': [artist1.name, artist2.name]
"type": "artist",
"ids": [artist1.pk, artist2.pk],
"names": [artist1.name, artist2.name],
}
assert filters.clean_config(config) == expected
def test_can_check_artist_filter(factories):
artist = factories['music.Artist']()
artist = factories["music.Artist"]()
assert filters.validate({'type': 'artist', 'ids': [artist.pk]})
assert filters.validate({"type": "artist", "ids": [artist.pk]})
with pytest.raises(ValidationError):
filters.validate({'type': 'artist', 'ids': [artist.pk + 1]})
filters.validate({"type": "artist", "ids": [artist.pk + 1]})
def test_can_check_operator():
assert filters.validate(
{'type': 'group', 'operator': 'or', 'filters': []})
assert filters.validate(
{'type': 'group', 'operator': 'and', 'filters': []})
assert filters.validate({"type": "group", "operator": "or", "filters": []})
assert filters.validate({"type": "group", "operator": "and", "filters": []})
with pytest.raises(ValidationError):
assert filters.validate(
{'type': 'group', 'operator': 'nope', 'filters': []})
assert filters.validate({"type": "group", "operator": "nope", "filters": []})

View file

@ -51,9 +51,9 @@ def test_can_pick_by_weight():
def test_can_get_choices_for_favorites_radio(factories):
files = factories['music.TrackFile'].create_batch(10)
files = factories["music.TrackFile"].create_batch(10)
tracks = [f.track for f in files]
user = factories['users.User']()
user = factories["users.User"]()
for i in range(5):
TrackFavorite.add(track=random.choice(tracks), user=user)
@ -71,56 +71,52 @@ def test_can_get_choices_for_favorites_radio(factories):
def test_can_get_choices_for_custom_radio(factories):
artist = factories['music.Artist']()
files = factories['music.TrackFile'].create_batch(
5, track__artist=artist)
artist = factories["music.Artist"]()
files = factories["music.TrackFile"].create_batch(5, track__artist=artist)
tracks = [f.track for f in files]
wrong_files = factories['music.TrackFile'].create_batch(5)
wrong_files = factories["music.TrackFile"].create_batch(5)
wrong_tracks = [f.track for f in wrong_files]
session = factories['radios.CustomRadioSession'](
custom_radio__config=[{'type': 'artist', 'ids': [artist.pk]}]
session = factories["radios.CustomRadioSession"](
custom_radio__config=[{"type": "artist", "ids": [artist.pk]}]
)
choices = session.radio.get_choices()
expected = [t.pk for t in tracks]
assert list(choices.values_list('id', flat=True)) == expected
assert list(choices.values_list("id", flat=True)) == expected
def test_cannot_start_custom_radio_if_not_owner_or_not_public(factories):
user = factories['users.User']()
artist = factories['music.Artist']()
radio = factories['radios.Radio'](
config=[{'type': 'artist', 'ids': [artist.pk]}]
)
user = factories["users.User"]()
artist = factories["music.Artist"]()
radio = factories["radios.Radio"](config=[{"type": "artist", "ids": [artist.pk]}])
serializer = serializers.RadioSessionSerializer(
data={
'radio_type': 'custom', 'custom_radio': radio.pk, 'user': user.pk}
data={"radio_type": "custom", "custom_radio": radio.pk, "user": user.pk}
)
message = "You don't have access to this radio"
assert not serializer.is_valid()
assert message in serializer.errors['non_field_errors']
assert message in serializer.errors["non_field_errors"]
def test_can_start_custom_radio_from_api(logged_in_client, factories):
artist = factories['music.Artist']()
radio = factories['radios.Radio'](
config=[{'type': 'artist', 'ids': [artist.pk]}],
user=logged_in_client.user
artist = factories["music.Artist"]()
radio = factories["radios.Radio"](
config=[{"type": "artist", "ids": [artist.pk]}], user=logged_in_client.user
)
url = reverse('api:v1:radios:sessions-list')
url = reverse("api:v1:radios:sessions-list")
response = logged_in_client.post(
url, {'radio_type': 'custom', 'custom_radio': radio.pk})
url, {"radio_type": "custom", "custom_radio": radio.pk}
)
assert response.status_code == 201
session = radio.sessions.latest('id')
assert session.radio_type == 'custom'
session = radio.sessions.latest("id")
assert session.radio_type == "custom"
assert session.user == logged_in_client.user
def test_can_use_radio_session_to_filter_choices(factories):
files = factories['music.TrackFile'].create_batch(30)
files = factories["music.TrackFile"].create_batch(30)
tracks = [f.track for f in files]
user = factories['users.User']()
user = factories["users.User"]()
radio = radios.RandomRadio()
session = radio.start_session(user)
@ -129,13 +125,13 @@ def test_can_use_radio_session_to_filter_choices(factories):
# ensure 30 differents tracks have been suggested
tracks_id = [
session_track.track.pk
for session_track in session.session_tracks.all()]
session_track.track.pk for session_track in session.session_tracks.all()
]
assert len(set(tracks_id)) == 30
def test_can_restore_radio_from_previous_session(factories):
user = factories['users.User']()
user = factories["users.User"]()
radio = radios.RandomRadio()
session = radio.start_session(user)
@ -144,37 +140,37 @@ def test_can_restore_radio_from_previous_session(factories):
def test_can_start_radio_for_logged_in_user(logged_in_client):
url = reverse('api:v1:radios:sessions-list')
response = logged_in_client.post(url, {'radio_type': 'random'})
session = models.RadioSession.objects.latest('id')
assert session.radio_type == 'random'
url = reverse("api:v1:radios:sessions-list")
response = logged_in_client.post(url, {"radio_type": "random"})
session = models.RadioSession.objects.latest("id")
assert session.radio_type == "random"
assert session.user == logged_in_client.user
def test_can_get_track_for_session_from_api(factories, logged_in_client):
files = factories['music.TrackFile'].create_batch(1)
files = factories["music.TrackFile"].create_batch(1)
tracks = [f.track for f in files]
url = reverse('api:v1:radios:sessions-list')
response = logged_in_client.post(url, {'radio_type': 'random'})
session = models.RadioSession.objects.latest('id')
url = reverse("api:v1:radios:sessions-list")
response = logged_in_client.post(url, {"radio_type": "random"})
session = models.RadioSession.objects.latest("id")
url = reverse('api:v1:radios:tracks-list')
response = logged_in_client.post(url, {'session': session.pk})
data = json.loads(response.content.decode('utf-8'))
url = reverse("api:v1:radios:tracks-list")
response = logged_in_client.post(url, {"session": session.pk})
data = json.loads(response.content.decode("utf-8"))
assert data['track']['id'] == tracks[0].id
assert data['position'] == 1
assert data["track"]["id"] == tracks[0].id
assert data["position"] == 1
next_track = factories['music.TrackFile']().track
response = logged_in_client.post(url, {'session': session.pk})
data = json.loads(response.content.decode('utf-8'))
next_track = factories["music.TrackFile"]().track
response = logged_in_client.post(url, {"session": session.pk})
data = json.loads(response.content.decode("utf-8"))
assert data['track']['id'] == next_track.id
assert data['position'] == 2
assert data["track"]["id"] == next_track.id
assert data["position"] == 2
def test_related_object_radio_validate_related_object(factories):
user = factories['users.User']()
user = factories["users.User"]()
# cannot start without related object
radio = radios.ArtistRadio()
with pytest.raises(ValidationError):
@ -187,59 +183,57 @@ def test_related_object_radio_validate_related_object(factories):
def test_can_start_artist_radio(factories):
user = factories['users.User']()
artist = factories['music.Artist']()
wrong_files = factories['music.TrackFile'].create_batch(5)
user = factories["users.User"]()
artist = factories["music.Artist"]()
wrong_files = factories["music.TrackFile"].create_batch(5)
wrong_tracks = [f.track for f in wrong_files]
good_files = factories['music.TrackFile'].create_batch(
5, track__artist=artist)
good_files = factories["music.TrackFile"].create_batch(5, track__artist=artist)
good_tracks = [f.track for f in good_files]
radio = radios.ArtistRadio()
session = radio.start_session(user, related_object=artist)
assert session.radio_type == 'artist'
assert session.radio_type == "artist"
for i in range(5):
assert radio.pick() in good_tracks
def test_can_start_tag_radio(factories):
user = factories['users.User']()
tag = factories['taggit.Tag']()
wrong_files = factories['music.TrackFile'].create_batch(5)
user = factories["users.User"]()
tag = factories["taggit.Tag"]()
wrong_files = factories["music.TrackFile"].create_batch(5)
wrong_tracks = [f.track for f in wrong_files]
good_files = factories['music.TrackFile'].create_batch(
5, track__tags=[tag])
good_files = factories["music.TrackFile"].create_batch(5, track__tags=[tag])
good_tracks = [f.track for f in good_files]
radio = radios.TagRadio()
session = radio.start_session(user, related_object=tag)
assert session.radio_type == 'tag'
assert session.radio_type == "tag"
for i in range(5):
assert radio.pick() in good_tracks
def test_can_start_artist_radio_from_api(
logged_in_api_client, preferences, factories):
artist = factories['music.Artist']()
url = reverse('api:v1:radios:sessions-list')
def test_can_start_artist_radio_from_api(logged_in_api_client, preferences, factories):
artist = factories["music.Artist"]()
url = reverse("api:v1:radios:sessions-list")
response = logged_in_api_client.post(
url, {'radio_type': 'artist', 'related_object_id': artist.id})
url, {"radio_type": "artist", "related_object_id": artist.id}
)
assert response.status_code == 201
session = models.RadioSession.objects.latest('id')
session = models.RadioSession.objects.latest("id")
assert session.radio_type == 'artist'
assert session.radio_type == "artist"
assert session.related_object == artist
def test_can_start_less_listened_radio(factories):
user = factories['users.User']()
wrong_files = factories['music.TrackFile'].create_batch(5)
user = factories["users.User"]()
wrong_files = factories["music.TrackFile"].create_batch(5)
for f in wrong_files:
factories['history.Listening'](track=f.track, user=user)
good_files = factories['music.TrackFile'].create_batch(5)
factories["history.Listening"](track=f.track, user=user)
good_files = factories["music.TrackFile"].create_batch(5)
good_tracks = [f.track for f in good_files]
radio = radios.LessListenedRadio()
session = radio.start_session(user)