So I am writing unittests for a project and I am testing register() function.
Here is it:
def register():
# Get information about user
username = request.get_json().get("username")
password = request.get_json().get("password")
name = request.get_json().get("name")
email = request.get_json().get("email")
# Put information about user in a tuple
values = (
None,
username,
User.hash_password(password),
name,
email,
None
)
try:
# Create user and update session
User(*values).create()
ActiveUser.logged_in = True
ActiveUser.username = username
info_log.info("User %s registered successfully." % username)
return jsonify(success=True, message="Registration successful!")
except pymongo.errors.DuplicateKeyError as e:
# Catch pymongo exception
return jsonify(success=False, message="Duplicated username or email!"), 403
I want to have three tests: valid, invalid (duplicate username), invalid (duplicate email).
# Register helper function
def register(self, username, password, name, email):
return self.app.post(
"/register",
data = json.dumps(dict(username = username, password = password, name = name, email = email)),
content_type='application/json',
follow_redirects = True
)
def test_02_valid_user_registration(self):
response = self.register('test', '12345678', 'Tester 1', 'test#mail.mail')
self.assertEqual(response.status_code, 200)
self.assertIn(b'Registration successful!', response.data)
def test_03_invalid_user_registration_duplicate_username(self):
response = self.register('test', '12345678', 'Tester 2', 'test1#mail.mail')
self.assertEqual(response.status_code, 403)
self.assertIn(b'Duplicate username or email!', response.data)
def test_04_invalid_user_registration_duplicate_email(self):
response = self.register('test2', '12345678', 'Tester 3', 'test#mail.mail')
self.assertEqual(response.status_code, 403)
self.assertIn(b'Duplicate username or email!', response.data)
As expected I get DuplicateKeyError, because I have set Unique for those parameters in the database.
pymongo.errors.DuplicateKeyError: E11000 duplicate key error collection: user.users index: username_1 dup key: { username: "test" }
Is there a way to get which is the duplicated item from the DuplicateKeyError, so I can have separate unit tests for duplicate username and email?
I know this is more of a component/integration testing rather than unit testing, but this is the only way I know how to do it in Python 3.
So I started digging through the implementation of DuplicateKeyError and I found that it contains code and details.
I printed the details of the error and got this:
{
"message": {
"code": 11000,
"errmsg": "E11000 duplicate key error collection: user.users index: username_1 dup key: {
username: \"test\"
}",
"index": 0,
"keyPattern": {
"username": 1
},
"keyValue": {
"username": "test"
}
},
"success": false
}
After that it was easy to get the two tests to work.
try:
# code
except pymongo.errors.DuplicateKeyError as e:
# Catch pymongo exception
key = list(e.details.get("keyValue").keys())[0]
value = e.details.get("keyValue").get(key)
return jsonify(success=False, message="Duplicate %s: %s" % (key, value)), 403
And the tests:
def test_03_invalid_user_registration_duplicate_username(self):
response = self.register("test", "12345678", "Tester 2", "test1#mail.mail")
self.assertEqual(response.status_code, 403)
self.assertIn(b"Duplicate username: test", response.data)
def test_04_invalid_user_registration_duplicate_email(self):
response = self.register("test", "12345678", "Tester 3", "test#mail.mail")
self.assertEqual(response.status_code, 403)
self.assertIn(b"Duplicate email: test#mail.mail", response.data)
Related
#endpoints.route('/login', methods=['POST'])
def login():
resp = {}
try:
users = user_collection
# login_user = users.find_one({'username': request.form['name']})
login_user = users.find({'username': request.form['username']})
if login_user:
if (request.form['password'] == login_user['password']):
session['username'] = request.form['username']
# return True
print("User logged in Successfully.")
print("Invalid username/password combination")
# return 'Invalid username/password combination'
status = {
"statusCode": "200",
"statusMessage": "User logged in Successfully."
}
except Exception as e:
print(e)
status = {
"statusCode": "400",
"statusMessage": str(e)
}
resp["status"] = status
return resp
I tried to submit POST request with JSON input from POSTMAN and expected the below response to be populated in POSTMAN response pane.
"statusCode": "200",
"statusMessage": "User logged in Successfully."
I'm trying to unit test the following function with pytest.
#login_sys.route("/users/register", methods=["POST"])
def register():
users = mongo.db.users # TRYING TO PATCH THIS
print("--->{}".format(type(users)))
email = request.get_json()["email"]
response = users.find_one({"email": email})
if response:
result = jsonify({"result": "email already registered"})
return result
first_name = request.get_json()["first_name"]
last_name = request.get_json()["last_name"]
password = bcrypt.generate_password_hash(request.get_json()["password"]).decode(
"utf-8"
)
created = datetime.utcnow()
user_id = users.insert(
{
"first_name": first_name,
"last_name": last_name,
"email": email,
"password": password,
"created": created,
}
)
new_user = users.find_one({"_id": user_id})
result = {"email": new_user["email"] + " registered"}
return jsonify({"result": result})
Following is the test I wrote
#pytest.fixture(scope="module")
def client():
app.config['TESTING'] = True
app.register_blueprint(login_sys)
with app.test_client() as client:
return client
def test_register(client, monkeypatch):
print(type(client))
mimetype = 'application/json'
headers = {
'Content-Type': mimetype,
'Accept': mimetype
}
userInfo = {
"first_name": "admin",
"last_name": "admin",
"email": "admin#gmail.com",
"password": "admin",
}
def mock_db():
return mongomock.MongoClient().db.collection
print(type(mongomock.MongoClient().db.collection))
#class'mongomock.collection.Collection'>
monkeypatch.setattr(mongo.db, 'users', mock_db)
rv = client.post("/users/register", data=json.dumps(userInfo), headers=headers)
dprint(rv.data)
assert True
I'm trying to replace the mongo.db.users with the fake one I made with mongomock mongomock.MongoClient().db.collection. However, when I ran this test, it shows an attribute error shown below:
E AttributeError: 'function' object has no attribute 'find_one'
I'm not sure why the mock_db() returns a type function instead of type <class'mongomock.collection.Collection'>
let me know if anyone has any suggestions.
Thank you.
here I am creating a new customer and save it into a customers.json file
new_user = appregister.newDocument('customers', username_1)
new_user.set('username', username_1)
new_user.set('email', email)
new_user.save()
new_user = format_customers()
print(new_user)
body_json = json.dumps(new_user)
scriptuser = util.get_app_parameter("scriptuser", "beta")
username = scriptuser["username"]
password = scriptuser["password"]
url = 'https://****.***.**/*****-****-1-0/****-****/customers.json'
headers = {'Content-Type': 'application/json'}
new_items = new_user
body_json = json.dumps(new_items)
status, headers, data = network.request(url, method='PUT',
headers=headers,
body=body_json,
encoding='UTF-8',
username=username,
password=password)
here I want to send a mail to the customer(like when i register a new customer so the customer get an email with some random code) I don't think that I am accessing the customer email
sources = ["customers.json"]
for source in sources:
data = util.get_data_item(source)
if data is None or len(data) == 0:
continue
register = json.loads(data)
for json_obj in register['customers']:
try:
emailaddress = json_obj['email']
print(email) # I get NONE here
except Exception as e:
emailaddress = None
if emailaddress:
if email == json_obj['email'].lower().strip():
code = "".join( [random.choice(string.digits) for i in xrange(6)] )
HTML = util.get_data_item("send_code2.html")
subject = (app_properties.get(EMAIL_TITLE_KEY))
try:
messageText = 'Your code is : '+ code
except Exception, e:
response = {'status' : 'error', 'message' : str(e)}
return json.dumps(response), responseHeaders, 400
p = Properties()
p.load(StringReader(util.get_data_item('messages.properties')))
title = (p.getProperty('email.title', ''))
color = json.loads(util.get_data_item('theme.json'))['colors']['tint'][-6:]
imagedata = base64.b64encode(util.get_data_item_bytes('logo.png'))
messageHtml = Environment().from_string(HTML).render(code=code, title=title, color=color, imagedata=imagedata)
try:
sendEmail('no-reply#ggg.com', email, subject, messageHtml, messageText)
except Exception, e:
logger.error("ERROR " + str(e))
response = {'status' : 'error', 'message' : str(e)}
return json.dumps(response), responseHeaders, 400
appRegister.put('activationcode', code);
appRegister.put('state', 'pending')
appRegister.put('activationtries', '0')
appRegister.put('email', email)
return json.dumps({}), responseHeaders, 200
error = {'title': app_properties[REGISTRATION_ERROR_TITLE_KEY], 'message' : app_properties[REGISTRATION_ERROR_KEY]}
return "200", responseHeaders
this is how my customers.json file looks like
{
"customers": {
"Osar": {
"email": "gmail#gmail.com"
},
"eeforetzr": {
"email": "email1#hotmail.com"
},
"aaxel": {
"email": "hotmail#hotmail.com"
}
"Mark": {
"email": "mark#gmail.com"
}
}
}
I am going to create chatbot using websockets. Each user can have their own account. I have Django backend and frontend written in Angular. At the moment I have a problem with message object. To wit I get this in backend:
django_1 | django.db.utils.IntegrityError: null value in column "user_id" violates not-null constraint
django_1 | DETAIL: Failing row contains (212, {"message":"Hello"}, null).
It looks as I wouldn't send user ID from frontend. Maybe I should send something like this {"message":"Hello", "id":1}? I wonder how can I solve it and how it should be done properly?
My Django message model looks in this way:
class Message(models.Model):
user = models.ForeignKey('auth.User')
message = models.CharField(max_length=200)
This is my backend consumer:
#channel_session_user_from_http
def msg_consumer(message):
text = message.content.get('text')
Message.objects.create(
message=text,
)
Group("chat").send({'text': text})
#channel_session_user
def ws_connect(message):
# Accept the connection
message.reply_channel.send({"accept": True})
# Add to the chat group
Group("chat").add(message.reply_channel)
message.reply_channel.send({
"text": json.dumps({
'message': 'Welcome'
})
})
#channel_session_user
def ws_receive(message):
message.reply_channel.send({"accept": True})
print("Backend received message: " + message.content['text'])
Message.objects.create(
message = message.content['text'],
)
Channel("chat").send({
"text": json.dumps({
'message': 'Can we start?'
})
})
#channel_session_user
def ws_disconnect(message):
Group("chat").discard(message.reply_channel)
This is part of my Angular component:
export class HomeComponent {
response: string;
response2: string;
constructor(
private chatService: ChatService,
private router: Router,
private http: Http,
) {
chatService.messages.subscribe(msg => {
this.response = msg.message;
console.log("Response from backend: " + msg.message);
});
}
private message = {
message: 'this is a test message'
}
sendMsg() {
console.log('new message from client to websocket: ', this.message);
this.chatService.messages.next(this.message);
return this.message.message;
}
send(msg) {
this.message.message = msg;
this.sendMsg();
}
login() {
return this.http.get('/data', )
.map(response => response.json())
.subscribe(response2 => this.response2 = response2);
}
}
#Component({
selector: 'key-up3',
template: `
<input #box (keyup.enter)="keyup7.emit(box.value)">
<p>{{value}}</p>
`
})
export class KeyUpComponent_v3 {
#Output() keyup7 = new EventEmitter<string>();
}
UPDATE
At the moment I solved it in the way shown below in backend.
def ws_receive(message):
message.reply_channel.send({"accept": True})
print("Backend received message: " + message.content['text'])
data = json.loads(message.content['text'])
Message.objects.create(
user_id=data['user_id'],
message = data['message'],
)
Error seems to indicate your Message instance was created without user_id.
What if you add this argument when creating a new Message instance ?
#channel_session_user_from_http
def msg_consumer(message):
text = message.content.get('text')
Message.objects.create(
user=message.user, # <- Here is the user associated
message=text,
)
Group("chat").send({'text': text})
I am using Batching requests in Google Analytics API(Python). Link to Batching : https://developers.google.com/api-client-library/python/guide/batch
Batching works fine when all the records via .add() are correct(valid). When one or more values are invalid, then the batching fails for all the records.
I added a call back function to handle the error and I saw that BAtching request is failing for all the records in the batch ( as opposed to only the invalid record). Is there a way to handle the error and skip the row/record which is invalid and continue with the rest of the records in the batch?
Below is the sample code I used and the error message :
def add_user_callback(request_id, response, exception):
if exception:
print "error :",exception
else:
print "successful"
def main():
## code to set the account, property and other variables
batch.add(service.management().webpropertyUserLinks().insert(
accountId=account_id,
webPropertyId=property_at,
body={
'permissions': {
'local': [
'READ_AND_ANALYZE'
]
},
'userRef': {
'email': 'valid_address#domain.com'
}
}))
batch.add(service.management().webpropertyUserLinks().insert(
accountId=account_id,
webPropertyId=property_at,
body={
'permissions': {
'local': [
'READ_AND_ANALYZE'
]
},
'userRef': {
'email': 'invalid_address#ddomain.com' ## i used a dummy id : pppp#domain.com
}
}))
batch.execute()
#Error :
#error : <HttpError 400 when requesting https://www.googleapis.com/analytics/v3/management/accounts/62974313/webproperties/UA-62974313-35/entityUserLinks?alt=json returned "Value for field user.email = ppppp#domain.com is not valid.">
#error : <HttpError 400 when requesting https://www.googleapis.com/analytics/v3/management/accounts/62974313/webproperties/UA-62974313-11/entityUserLinks?alt=json returned "Value for field user.email = ppppp#domain.com is not valid.">
Please let me know if you need more info.
Let's assume you have a list of users you want to add to profiles stored in a list users.
You can remove the bad emails with the following callback function:
def call_back(request_id, response, exception):
if exception is not None:
if isinstance(exception, HttpError):
message = json.loads(exception.content)['error']['message']
bad = 'Value for field user.email = (\S*) is not valid.'
match = re.match(bad, message)
if match:
bad_user = match.group(1)
if bad_user in users:
users.remove(bad_user)
else:
print response
After all the failed calls return you can re-attempt the batch call again by looping through the users and constructing a new batch request:
batch = BatchHttpRequest(callback=call_back)
for user in users:
request = analytics.management().profileUserLinks().insert(
accountId=ACCOUNT_ID,
webPropertyId=PROFILE_ID,
profileId=profile,
body={
'permissions': {'local': ['READ_AND_ANALYZE']},
'userRef': {'email': user}
}
)
batch.add(request, request_id=PROFILE_ID + user)
batch.execute()