def test_login_redirect(monkeypatch): app = BetterTestApp(server.app) from chatty.util import fb mocktokencheck = MagicMock(return_value=({ "app_id": "dummy", "user_id": 123456 }, "tokentoken")) monkeypatch.setattr(fb, "login_redirect_token_check", mocktokencheck) monkeypatch.setattr( fb, "profile_detail", MagicMock(return_value={'age_range': { "min": 18, "max": 21 }})) uidenc = uidcrypt.encryptuid("1878285869108664") s = Session() s.add(User(1878285869108664)) s.commit() s.close() tok = tokener.gen_token("1878285869108664", tokener.NS_LOGIN) r = app.get("/bot/fb/signin_redirect?uid={}&state={}#tototoken".format( uidenc, tok)) assert r.status_int == 200 u = User.get("1878285869108664") assert u.id == "1878285869108664" assert u.fb_id == "123456" assert u.age_min == 18 assert u.age_max == 21 assert u.signup_date is not None
def test_payments_invalid_menu(monkeypatch): app = BetterTestApp(server.app) from chatty.libs import slack import stripe slackpost = MagicMock() mock_customer_create = MagicMock(return_value=MagicMock(id="customerid")) mock_charge_create = MagicMock(return_value=MagicMock(id="chargeid")) monkeypatch.setattr(slack, "post", slackpost) monkeypatch.setattr(stripe.Customer, "create", mock_customer_create) monkeypatch.setattr(stripe.Charge, "create", mock_charge_create) User.create("1878285869108664", fb_id="testfbid") uidenc = uidcrypt.encryptuid("1878285869108664") tok = tokener.gen_token("1878285869108664", tokener.NS_PAYMENT_SECOND) r = app.post_json("/" + uidenc + "/payments?access_token=" + tok, {"token": "stripetoken", "points": 500, "price": 5, "currency": "USD"}, status=400) assert slackpost.call_count == 0 assert mock_customer_create.call_count == 0 assert mock_charge_create.call_count == 0 assert User.get("1878285869108664").points == 20 assert PaymentSource.get("testfbid") is None assert len(Payment.get("testfbid")) == 0 assert r.status == "400 Bad Request" assert "expiration" in r.json['text']
def test_on_match_increment_speech(mocker, monkeypatch): fix_matcher_rand(mocker) delegate = UserStateDelegate() user = User.get("test1") partner = User.get("test2") time = datetime.datetime.utcnow() record = MatchingQueue(created_at=time, finding_genders="male,female") uc, pc = [UserContext(u) for u in [user, partner]] delegate.on_found_partner(uc, pc, record) m1 = Match.get("test1") assert m1.founder_speech_count == 0 assert m1.waiter_speech_count == 0 delegate.on_speak(uc) assert m1.founder_speech_count == 1 assert m1.waiter_speech_count == 0 delegate.on_speak(uc) assert m1.founder_speech_count == 2 assert m1.waiter_speech_count == 0 delegate.on_speak(pc) assert m1.founder_speech_count == 2 assert m1.waiter_speech_count == 1 delegate.on_speak(pc) assert m1.founder_speech_count == 2 assert m1.waiter_speech_count == 2
def test_on_match_end(mocker, monkeypatch): fix_matcher_rand(mocker) delegate = UserStateDelegate() user = User.create("test1", finding_genders="female", gender="male", points=30) partner = User.create("test2", finding_genders="female,male", gender="female", points=30) time = datetime.datetime.utcnow() record = MatchingQueue(created_at=time, finding_genders="male,female") uc, pc = [UserContext(u) for u in [user, partner]] delegate.on_found_partner(uc, pc, record) delegate.on_speak(uc) delegate.on_speak(uc) delegate.on_speak(pc) delegate.on_match_ended(uc, pc) assert Match.get("test1") is None assert Match.get("test2") is None res = MatchHistory.get("test1") assert len(res) == 1 assert res[0].founder_id == "test1" assert res[0].waiter_id == "test2" assert res[0].founder_speech_count == 2 assert res[0].waiter_speech_count == 1 assert res[0].founder_used_points == 10 assert res[0].waiter_used_points == 0 assert res[0].waited_at == time assert res[0].ended_by == "test1" assert res[0].started_at > time
def test_translate_for_user(): user = User("testtest") user.locale = "en_US" uc = UserContext(user) assert uc._("New Chat") == "New Chat" user.locale = "fr_FR" assert uc._("New Chat") == "Nouveau chat" user.locale = "ar_AR" assert uc._("New Chat") == "دردشة جديدة"
def test_payments_faild(monkeypatch): from chatty.domain import payment from chatty.libs import slack import stripe mockpost = MagicMock() mock_customer_create = MagicMock(return_value=MagicMock(id="customerid")) mock_charge_create = MagicMock(return_value=MagicMock(id="chargeid1")) monkeypatch.setattr(slack, "post", mockpost) monkeypatch.setattr(stripe.Customer, "create", mock_customer_create) monkeypatch.setattr(stripe.Charge, "create", mock_charge_create) session = Session() User.create("test1", fb_id="testfbid") session.commit() session.close() def assert_no_effect(): assert User.get("test1").points == 20 assert PaymentSource.get("testfbid") is None assert len(Payment.get("testfbid")) == 0 User.get("test1").points = 3999 with pytest.raises(HTTPError) as e: payment.pay_from_form("test1", "stripetoken", 500, 495, "USD") assert "Limit Exceeded" in e.message assert mock_customer_create.call_count == 0 assert mock_charge_create.call_count == 0 assert_no_effect() with pytest.raises(HTTPError) as e: payment.pay_from_form("test1", "stripetoken", 500, 4, "USD") assert "menu might be changed" in e.message assert mock_customer_create.call_count == 0 assert mock_charge_create.call_count == 0 assert_no_effect() with pytest.raises(HTTPError) as e: payment.pay_from_form("test1", "stripetoken", 500, 495, "other") assert "menu might be changed" in e.message assert mock_customer_create.call_count == 0 assert mock_charge_create.call_count == 0 mock_customer_create.side_effect = stripe.error.CardError("e", {}, 400) with pytest.raises(HTTPError) as e: payment.pay_from_form("test1", "stripetoken", 500, 495, "USD") assert payment.MESSAGE_CONTACT_CREDIT in e.message assert_no_effect() mock_customer_create.side_effect = None mock_charge_create.side_effect = stripe.error.CardError("e", {}, 400) with pytest.raises(HTTPError) as e: payment.pay_from_form("test1", "stripetoken", 500, 495, "USD") assert payment.MESSAGE_CONTACT_CREDIT in e.message assert_no_effect()
def test_payments_invalid_token(monkeypatch): app = BetterTestApp(server.app) User.create("1878285869108664", fb_id="testfbid") uidenc = uidcrypt.encryptuid("1878285869108664") tok = tokener.gen_token("1878285869108664", tokener.NS_PAYMENT_SECOND) r = app.post_json( "/" + uidenc + "/payments?access_token=" + "invalidtoken", {"token": "stripetoken", "points": 500, "price": 5, "currency": "USD"}, status=400) assert User.get("1878285869108664").points == 20 assert PaymentSource.get("testfbid") is None assert len(Payment.get("testfbid")) == 0 assert r.status == "400 Bad Request" assert "expiration of token" in r.json['text']
def test_login_failed(monkeypatch): app = BetterTestApp(server.app) from chatty.util import fb mocksend = MagicMock() monkeypatch.setattr(fb, "send_data", mocksend) uid = uidcrypt.encryptuid("1878285869108664") app.get("/bot/fb/signin_redirect?uid={}&state={}#tototoken".format( uid, "dummy"), status=400) assert mocksend.call_count == 0 s = Session() s.add(User(1878285869108664)) s.commit() s.close() app.get("/bot/fb/signin_redirect?uid={}&state={}#tototoken".format( uid, "dummy"), status=400) assert mocksend.call_count == 1 assert_button( mocksend.call_args_list[0][0][0], "1878285869108664", "Please log in with", [fb.web_button("", "www.facebook.com/v2.8/dialog/oauth", "", "")])
def test_payments(monkeypatch): from chatty.domain import payment from chatty.libs import slack import stripe mockpost = MagicMock() mock_customer_create = MagicMock(return_value=MagicMock(id="customerid")) mock_charge_create = MagicMock(return_value=MagicMock(id="chargeid1")) monkeypatch.setattr(slack, "post", mockpost) monkeypatch.setattr(stripe.Customer, "create", mock_customer_create) monkeypatch.setattr(stripe.Charge, "create", mock_charge_create) User.create("test1", fb_id="testfbid") assert User.get("test1").points == 20 payment.pay_from_form("test1", "stripetoken", 500, 495, "USD") assert mock_customer_create.call_count == 1 assert mock_charge_create.call_count == 1 ps = PaymentSource.get("testfbid") assert ps.user_id == "testfbid" assert ps.stripe_id == "customerid" assert ps.created_at is not None payments = Payment.get("testfbid") assert len(payments) == 1 assert payments[0].id == "chargeid1" assert payments[0].user_id == "testfbid" assert payments[0].stripe_user_id == "customerid" assert payments[0].points == 500 assert payments[0].price == 495 assert payments[0].currency == "usd" mock_charge_create().id = "chargeid2" payment.pay_from_form("test1", "stripetoken", 1500, 21383, "EGP") ps = PaymentSource.get("testfbid") assert ps.user_id == "testfbid" assert ps.stripe_id == "customerid" assert ps.created_at is not None payments = Payment.get("testfbid") assert len(payments) == 2 assert payments[1].id == "chargeid2" assert payments[1].user_id == "testfbid" assert payments[1].stripe_user_id == "customerid" assert payments[1].points == 1500 assert payments[1].price == 21383 assert payments[1].currency == "egp"
def log(uidenc): uid = uidcrypt.decryptuid(uidenc) json = request.json json['uid'] = uid try: json['locale'] = User.get(uid).locale except Exception: logger.exception("faild to get ui lang ") logger.info("kpi_action:{}".format(json))
def test_matcher(): assert Matcher().randomnum != Matcher().randomnum matcher = Matcher(randomnum=1) assert matcher.randomnum == 1 user1 = User.create("u1", finding_genders="female,male", gender="male") user2 = User.create("u2", finding_genders="female,male", gender="female") user3 = User.create("u3", finding_genders="female", gender="female") user4 = User.create("u4", finding_genders="female", gender="male") cache = {'my_langs': set([Language.get("en_US")])} assert not matcher.should_match(user1, user2, cache, False) assert not matcher.should_match(user2, user3, cache, False) assert not matcher.should_match(user1, user4, cache, False) matcher.randomnum = 0 assert matcher.should_match(user1, user2, cache, False) assert matcher.should_match(user2, user3, cache, False) assert not matcher.should_match(user1, user3, cache, False) assert not matcher.should_match(user1, user4, cache, False)
def test_on_found_partner(mocker, monkeypatch): delegate = UserStateDelegate() fix_matcher_rand(mocker) user = User.create("test1", finding_genders="male,female", gender="male", points=30) partner = User.create("test2", finding_genders="male,female", gender="male", points=30) uc, pc = [UserContext(u) for u in [user, partner]] record = MatchingQueue(created_at=datetime.datetime.utcnow(), finding_genders="male,female") delegate.on_found_partner(uc, pc, record) assert user.points == 30 assert partner.points == 30 assert Match.get("test1").founder_used_points == 0 delegate.on_match_ended(uc, pc) user.finding_genders = "female" partner.gender = "female" delegate.on_found_partner(uc, pc, record) assert user.points == 20 assert partner.points == 30 assert Match.get("test1").founder_used_points == 10 assert Match.get("test1").waiter_used_points == 0 delegate.on_match_ended(uc, pc) delegate.on_found_partner(pc, uc, record) assert user.points == 20 assert partner.points == 30 record.finding_genders = "female" delegate.on_match_ended(uc, pc) delegate.on_found_partner(pc, uc, record) assert user.points == 10 assert partner.points == 30 assert Match.get("test1").founder_used_points == 0 assert Match.get("test1").waiter_used_points == 10
def test_on_try_search_on_banned(mocker, monkeypatch): fix_matcher_rand(mocker) delegate = UserStateDelegate() uc = UserContext(User.create("test1", "male,female", "male", 30)) us = uc.status from chatty.util import fb mocksend = MagicMock() monkeypatch.setattr(fb, "send_data", mocksend) black_list.ban_user("test1") assert not delegate.on_try_search(us) assert mocksend.call_count == 1 assert_message_is(mocksend.call_args_list[0][0][0], "test1", "suspended")
def test_ban_user(monkeypatch): app = BetterTestApp(server.app) from chatty.libs import slack mockpost = MagicMock() monkeypatch.setattr(slack, "post", mockpost) s = Session() s.add(User(1878285869108664)) s.commit() s.close() tok = tokener.gen_token_invert("1878285869108664", tokener.NS_BAN_USER) r = app.get("/ban_user/" + tok + "/confirm") assert mockpost.call_count == 1 assert black_list.is_banned("1878285869108664") assert r.status == '200 OK'
def test_post_settings(monkeypatch): import requests mock_request(monkeypatch, requests) app = BetterTestApp(server.app) res = app.post( "/VVaEZ1RQh3EvlGJqj7m1NiD6oF3UrxAM8c1oeWh1UQ0=/settings", { "languages": ["en_US", "fr_FR"], "genders": ["male", "female"], "longitude": 139.11, "latitude": 35.11, "location_enabled": "true", "search_radius": 350 }) assert res.status == "200 OK" user = User.get("1183739191739808") assert user.languages[0] == Language.get("en_US") assert user.languages[1] == Language.get("fr_FR") assert len(user.languages) == 2 assert user.finding_genders == "male,female" assert user.current_longitude == 139.11 assert user.current_latitude == 35.11 assert user.location_enabled == True assert user.search_radius == 350 res = app.post("/VVaEZ1RQh3EvlGJqj7m1NiD6oF3UrxAM8c1oeWh1UQ0=/settings", { "languages": ["en_US"], "genders": ["male"], "location_enabled": "false", }) user = User.get("1183739191739808") assert res.status == "200 OK" assert user.languages[0] == Language.get("en_US") assert len(user.languages) == 1 assert user.finding_genders == "male" assert user.location_enabled == False
def get_settings(user_id): user = User.get(user_id) if user: return dict( languages=list([lang.id for lang in user.languages]), finding_genders=user.finding_genders.split(","), location_enabled=user.location_enabled, country=user.country or "undefined", current_latitude=user.current_latitude, current_longitude=user.current_longitude, search_radius=user.search_radius or 500, ) return dict(languages=[], finding_genders=["male", "female"], location_enabled=False, country="undefined", current_latitude=None, current_longitude=None, search_radius=500)
def test_on_try_search(mocker, monkeypatch): fix_matcher_rand(mocker) delegate = UserStateDelegate() uc = UserContext(User.create("test1", "male,female", "male", 30)) us = uc.status from chatty.util import fb mocksend = MagicMock() monkeypatch.setattr(fb, "send_data", mocksend) assert delegate.on_try_search(us) assert mocksend.call_count == 0 us.user.points = 0 assert delegate.on_try_search(us) assert mocksend.call_count == 0 us.user.finding_genders = "female" assert not delegate.on_try_search(us) assert mocksend.call_count == 1 assert_button(mocksend.call_args_list[0][0][0], "test1", "enough points", [fb.web_button("", "/buy_points", "", "")])
def do_action(uid): if not User.exists(uid): return UserContext.get(uid).do_action({})
def pay_from_form(puid, token, points, price, currency): if not MenuPrice.is_valid(points, price, currency): abort( 400, "Payment failed due to expiration of token. Please refresh your web browser and then try again." ) astr = "{} {} {} {}".format(puid, token, points, price, currency) def translate(text): return uc._(text) if uc is not None else text try: user = User.get(puid) uc = UserContext(user) if user.points + points > 4000: abort(400, MESSAGE_TOO_MATCH_POINTS) customer = stripe.Customer.create(source=token, metadata={ 'puid': puid, 'fb_id': user.fb_id }) PaymentSource.create(user.fb_id, customer.id) charge = stripe.Charge.create( amount=price, currency=currency.lower(), description="Chatty: {} points".format(points), customer=customer.id, metadata={ 'puid': puid, 'fb_id': user.fb_id }) user.points += int(points) Payment.create(charge.id, user.fb_id, customer.id, points, price, currency.lower()) # https://stripe.com/docs/api#error_handling # https://stripe.com/docs/declines#decline-codes except stripe.error.CardError as e: # Since it's a decline, stripe.error.CardError will be caught logger.error("Failed payment {} {} ".format(astr, e.json_body)) abort(400, translate(MESSAGE_CONTACT_CREDIT)) except stripe.error.RateLimitError as e: # Too many requests made to the API too quickly abort(400, translate(MESSAGE_CONTACT_US)) except stripe.error.InvalidRequestError as e: # Invalid parameters were supplied to Stripe's API slack.post("Failed payment {} {}".format(astr, e.json_body)) abort(400, translate(MESSAGE_CONTACT_US)) except stripe.error.AuthenticationError as e: slack.post("Failed payment {} {}".format(astr, e.json_body)) abort(400, translate(MESSAGE_CONTACT_US)) except stripe.error.APIConnectionError as e: # Network communication with Stripe failed abort(400, translate(MESSAGE_CONTACT_US)) except stripe.error.StripeError as e: slack.post("Failed payment {} {}".format(astr, e.json_body)) # Display a very generic error to the user, and maybe send # yourself an email abort(400, translate(MESSAGE_CONTACT_US)) except HTTPError as e: raise e except Exception as e: slack.post("Failed payment {} {}".format(astr, e.message)) # Something else happened, completely unrelated to Stripe abort(400, translate(MESSAGE_CONTACT_US)) return user.points
def get(cls, user_id): return UserContext(User.get(user_id))
def reset_fb_id(user_id): user = User.get(user_id) user.fb_id = None
def reset_gender(user_id): user = User.get(user_id) user.gender = None user.locale = None user.country = None user.fb_id = None
def reset_last_matched(user_id): user = User.get(user_id) user.last_matched = None
def assert_no_effect(): assert User.get("test1").points == 20 assert PaymentSource.get("testfbid") is None assert len(Payment.get("testfbid")) == 0
def test_user_state_check_error(mocker, monkeypatch, caplog): fix_matcher_rand(mocker) from chatty.domain.user_state import ( User, IdleUserState, SearchingUserState, TalkingUserState, ) from chatty.domain.model.match import Language import requests from chatty.util.fb import requests as fbreq mock_request(monkeypatch, requests) mock_request(monkeypatch, fbreq) # session session = model.Session() # ユーザーの言語設定を行う english = (session.query(Language).get('en_US') or Language(id='en_US', name='English')) user = User('TEST-01', 'TEST-01') user.languages = [english] partner = User('TEST-02', 'TEST-02') partner.languages = [english] session.add(user) session.add(partner) session.commit() user_c = UserContext(user) partner_c = UserContext(partner) # logging 設定 caplog.set_level(logging.INFO) # エラーが返ってきた場合ワーニングログを出力 mock_request(monkeypatch, fbreq, {"error": { "code": 190, "error_subcode": 1234567, }}) user_c.status.send_message('message', action.IDLE__MESSAGE) # warning log が出力されているはず for rec in caplog.records: print('log: ', rec) if (rec.levelname == 'WARNING' and rec.message.startswith('Facebook send api error:')): break else: assert False, 'Not found error message' session.commit() # メッセージ送信先がメッセージを受信できない場合は検索,会話状態から削除する # 検索状態へ normalres = { 'message_id': 'mid.1484759787552:e29cca0c16', 'recipient_id': '1261793003907488' } mock_request(monkeypatch, fbreq, normalres) user_c.do_search(None) session.commit() assert isinstance(user_c.status, SearchingUserState) mock_request( monkeypatch, fbreq, {"error": { "code": 10, "error_subcode": 2018108, }}, ) user_c.status.send_message('message', action.SEARCHING__MESSAGE) session.commit() # アイドル状態になっているはず assert isinstance(user_c.status, IdleUserState) # 会話状態へ mock_request(monkeypatch, fbreq, normalres) user_c.do_search(None) partner_c.do_search(None) session.commit() assert isinstance(user_c.status, TalkingUserState) assert isinstance(partner_c.status, TalkingUserState) def m(url, json): recipient_ = json['recipient']['id'] if recipient_ == "TEST-01": return MagicMock(json=MagicMock(return_value=normalres)) elif recipient_ == "TEST-02": return MagicMock(json=MagicMock( return_value={ "error": { "code": 200, "error_subcode": 1545041, } })) raise ValueError( "dummy recipient must be TEST-01 or TEST-02 but {}".format( recipient_)) monkeypatch.setattr(fbreq, 'post', m) user_c.status.do_message( MagicMock(message=MagicMock(text="hello", _fields=["text"]))) session.commit() # アイドル状態になっているはず assert isinstance(user_c.status, IdleUserState) assert isinstance(partner_c.status, IdleUserState)
def test_match_location(mocker, monkeypatch): fix_matcher_rand(mocker) from chatty.domain.user_state import ( User, ) from chatty.domain.model.match import Language import requests mock_request(monkeypatch, requests) session = Session() english = (session.query(Language).get('en_US') or Language(id='en_US', name='English')) user1 = User('TEST-01') user1.languages = [english] user2 = User('TEST-02') user2.languages = [english] session.add(user1) session.add(user2) session.commit() cache = dict(my_langs=set([english])) mat = matcher.get() # default, both don't use location assert mat.should_match(user1, user2, cache, True) user1.location_enabled = True # user1 wanna use location but don't have, so ignore. # user2 doesn't care location. match. assert mat.should_match(user1, user2, cache, True) # at TokyoTocho user1.current_longitude = 139.4130 user1.current_latitude = 35.4122 assert not mat.should_match(user1, user2, cache, True) assert mat.should_match(user1, user2, cache, False) # at OsakaShiyakusho user2.current_longitude = 135.3008 user2.current_latitude = 34.4138 # user2 is in user1 radius 400, and user2 doesn't care radius assert mat.should_match(user1, user2, cache, True) user2.location_enabled = True assert mat.should_match(user1, user2, cache, True) user2.search_radius = 100 assert not mat.should_match(user1, user2, cache, True) assert mat.should_match(user1, user2, cache, False) user1.current_longitude = 136.3008 user1.current_latitude = 34.4138 assert mat.should_match(user1, user2, cache, True)
def test_state_transition(mocker, monkeypatch): """状態遷移のテスト""" fix_matcher_rand(mocker) from chatty.domain.user_state import ( User, IdleUserState, SearchingUserState, TalkingUserState, ) from chatty.domain.model.match import Language import requests mock_request(monkeypatch, requests) # session session = model.Session() # ユーザーの言語設定を行う english = (session.query(Language).get('en_US') or Language(id='en_US', name='English')) user1 = User('TEST-01') user1.languages = [english] user2 = User('TEST-02', 'TEST-02') user2.languages = [english] user3 = User('TEST-03', 'TEST-03') user3.languages = [english] session.add(user1) session.add(user2) session.commit() user1_c = UserContext(user1) user2_c = UserContext(user2) user3_c = UserContext(user3) # At first, must be NoSignUpIdle assert isinstance(user1_c.status, NoSignUpIdleUserState) user1.fb_id = "1234" assert isinstance(user1_c.status, IdleUserState) # ユーザをサーチ状態にする user1_c.do_search(None) session.commit() assert isinstance(user1_c.status, SearchingUserState) # ユーザーを会話状態にする user2_c.do_search(None) session.commit() assert isinstance(user1_c.status, TalkingUserState) assert isinstance(user2_c.status, TalkingUserState) # 会話を終了する user2_c.end_conversation_and_search(None) session.commit() assert isinstance(user1_c.status, IdleUserState) assert isinstance(user2_c.status, SearchingUserState) # not match same tuple user1_c.do_search(None) session.commit() assert isinstance(user1_c.status, SearchingUserState) assert isinstance(user2_c.status, SearchingUserState) # match with pother partners. user3_c.do_search(None) session.commit() assert isinstance(user1_c.status, SearchingUserState) assert isinstance(user2_c.status, TalkingUserState) assert isinstance(user3_c.status, TalkingUserState) # 会話を終了する user2_c.end_conversation(None) session.commit() assert isinstance(user1_c.status, SearchingUserState) assert isinstance(user2_c.status, IdleUserState) assert isinstance(user3_c.status, IdleUserState) user1_c.cancel_search(None) session.commit() assert isinstance(user1_c.status, IdleUserState)