DB not changing when testing Flask-Security with peewe and PyTest - testing

I just got into testing my flask application with pytest, and it mostly works as expected. Unfortunately the test uses the live DB instead a mock one. I'm quite sure this has to do with the fact, that flask-security is using peewee's database_wrapper instead of an "straightforward" database.
Here's some code. This is from the test:
#pytest.fixture
def client():
db_fd, belavoco_server.app.config['DATABASE'] = { 'name': 'userLogin_TEST.db',
'engine': 'peewee.SqliteDatabase' } }
belavoco_server.app.config['TESTING'] = True
client = belavoco_server.app.test_client()
#this seems not to help at all
with belavoco_server.app.app_context():
belavoco_server.users.user_managment.db_wrapper.init_app(belavoco_server.app)
yield client
os.close(db_fd)
os.unlink(belavoco_server.app.config['DATABASE'])
This is some code from my bv_user_model.py
app.config['DATABASE'] = {
'name': 'userLogin.db',
'engine': 'peewee.SqliteDatabase',
}
app.config['SECURITY_URL_PREFIX'] = "/users"
# Create a database instance that will manage the connection and
# execute queries
db_wrapper = FlaskDB(app)
class Role(db_wrapper.Model, RoleMixin):
name = CharField(unique=True, default="standard")
description = TextField(null=True)
def __repr__(self):
return self.name
When preforming the test, Flask is using the userLogin.db instead of userLogin_TEST.db. I suppose this is because of the db_wrapper in bv_user_model.py - but I did not find a way to change this behaviour. Any help would be greatly appreciated!

The root of the issue seems to be this in bv_user_model:
app.config['DATABASE'] = {
'name': 'userLogin.db',
'engine': 'peewee.SqliteDatabase',
}
Since you are using FlaskDB with the app that has the production credentials, it seems like the db_wrapper will "remember" that and not be overridden by your tests.
The most straightforward answer would be to not use your app to create the FlaskDB instance directly
db = FlaskDB()
And then later on initialize it on your app
from models import db
def create_app():
app = ...
app.config["DATABASE"] = ...
db.init_app(app)
...
return app
Which would let you have a separate function like this which you can use for testing.
def create_test_app():
app = ...
app.config["DATABASE"] = ...test credentials...
db.init_app(app)
...
return app
and when you create your models, use the FlaskDB instance just the same as you were already.
db = FlaskDB()
class Role(db.Model, RoleMixin):
...

Related

Flask-Appbuilder - User security role required in View

If the current user role = admin then show all the records in the table. If not, then limit the rows by created user.
I can get the user name if I define a function in the View Class, but need it before the list is constructed. See source code below.
from flask_appbuilder.models.sqla.filters import FilterEqualFunction
from app import appbuilder, db
from app.models import Language
from wtforms import validators, TextField
from flask import g
from flask_appbuilder.security.sqla.models import User
def get_user():
return g.user
class LanguageView(ModelView):
datamodel = SQLAInterface(Language)
list_columns = ["id", "name"]
base_order = ("name", "asc")
page_size = 50
#This is the part that does not work - unable to import app Error: Working outside of application context
#If the user role is admin show all, if not filter only to the specific user
if g.user.roles != "admin":
base_filters = [['created_by', FilterEqualFunction, get_user]]
This is the error I'm getting:
Was unable to import app Error: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
In this case it is better to create two different ModelViews, one for users with base_filters = [['created_by', FilterEqualFunction, get_user]] and the second for admins only without any filtering.
And do not forget to specify correct permissions for both.
In my case, I created new filter FilterStartsWithFunction by coping FilterStartsWith(You can find the source code in Flask-Appbuilder pack easily. ). see the codes
from flask_appbuilder.models.sqla.filters import get_field_setup_query
class FilterStartsWithFunction(BaseFilter):
name = "Filter view with a function"
arg_name = "eqf"
def apply(self, query, func):
query, field = get_field_setup_query(query, self.model, self.column_name)
return query.filter(field.ilike(func() + "%"))
def get_user():
if 'Admin' in [r.name for r in g.user.roles]:
return ''
else:
return g.user.username
...
...
base_filters = [['created_by',FilterStartsWithFunction,get_user]]

POST a list to the API, update or create depending on the existence of that instance

I have a view which allows me to post multiple entries to a model. Currently if I add all new entries, they are added successfully. But if I add any entry for which the pk already exists, it naturally throws a serializer error and nothing gets updated.
I wish to write a method which will let me post multiple entries, but automatically either update an existing one OR add a new one successfully depending the existence of that instance.
The idea of a customized ListSerialzer is the closest thing I came across to achieve this but still not clear if I can do this.
Has anyone ever implemented anything like this ?
In views.py:
def post(self,request,format=None):
data = JSONParser().parse(request)
serializer = PanelSerializer(data=data,many=True)
if serializer.is_valid():
serializer.save()
return JsonResponse({"success":"true","content":serializer.data}, status=201)
return JsonResponse({'success': "false",'errorCode':"1",'errorMessage':serializer.errors}, status=400)
in serializers.py:
class PanelSerializer(serializers.ModelSerializer):
class Meta:
model = Panel
fields = ('pId','pGuid','pName', 'pVoltage', 'pAmperage','pPermission', 'pPresent', 'pSelf','pInfo')
def create(self, validated_data):
logger.info('Information incoming_1!')
print ("Atom")
return Panel.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.pId = validated_data.get('pId', instance.pId)
instance.pGuid = validated_data.get('pId', instance.pGuid)
instance.pName = validated_data.get('pName', instance.pName)
instance.pVoltage = validated_data.get('pVoltage', instance.pVoltage)
instance.pAmperage = validated_data.get('pAmperage', instance.pAmperage)
instance.pPermission = validated_data.get('pPermission', instance.pPermission)
instance.pPresent = validated_data.get('pPresent', instance.pPresent)
instance.pSelf = validated_data.get('pSelf', instance.pSelf)
instance.pInfo = validated_data.get('pInfo', instance.pInfo)
instance.save()
return instance
This is how the code stands as of now. I believe I will mainly need to either work on the update method of my serializer or first change it to a ListSerializer and write some custom logic again in the update method.

How to mock the 'new' operator

I'm testing some groovy code that uses a java library and I want to mock out the library calls because they use the network. So the code under test looks something like:
def verifyInformation(String information) {
def request = new OusideLibraryRequest().compose(information)
new OutsideLibraryClient().verify(request)
}
I tried using MockFor and StubFor but I get errors such as:
No signature of method: com.myproject.OutsideLibraryTests.MockFor() is applicable for argument types: (java.lang.Class) values: [class com.otherCompany.OusideLibraryRequest]
I'm using Grails 2.0.3.
I've just found that we can always overwrite a constructor via MetaClass, as Grails 2 will be reset MetaClass modification at the end of each test.
This trick is better than Groovy's MockFor. AFAIK, Groovy's MockFor does not allow us to mock JDK's classes, java.io.File, for example. However in the below example, you cannot use File file = new File("aaa") as the real object type is a Map, not a File. The example is a Spock specification.
def "test mock"() {
setup:
def fileControl = mockFor(File)
File.metaClass.constructor = { String name -> [name: name] }
def file = new File("aaaa")
expect:
file.name == "aaaa"
}
The second, optional parameter to MockFor's constructor is interceptConstruction. If you set this to true, you can mock the constructor. Example:
import groovy.mock.interceptor.MockFor
class SomeClass {
def prop
SomeClass() {
prop = "real"
}
}
def mock = new MockFor(SomeClass, true)
mock.demand.with {
SomeClass() { new Expando([prop: "fake"]) }
}
mock.use {
def mockedSomeClass = new SomeClass()
assert mockedSomeClass.prop == "fake"
}
Note, however, you can only mock out groovy objects like this. If you're stuck with a Java library, you can pull the construction of the Java object into a factory method and mock that.

SQL connection in Grails

In my application there are a few places where I want to use pure SQL. When I hard code the connection details in Sql.newInstance it works fine. For obvious reasons I would prefer to not hard code the connection details.
When I use the dataSource variable it comes up as null. My code in the controller is:
import groovy.sql.Sql
def dataSource
def sql = Sql.newInstance(dataSource)
sql.executeInsert("insert into....")
My code in the datasource config file is:
environments {
development {
dataSource {
dbCreate = "create-drop"
url = "jdbc:postgresql:mydev"
username = "xxx"
password = "xxx"
As you can see I'm using PostgreSQL. I've also tried it using the default grails database with the same results.
Any ideas would be appreciated.
You need to define dataSource outside of your controller action. Otherwise spring cannot do the required dependency injection for you:
class YouController {
def dataSource
def yourAction() {
def sql = new Sql(dataSource)
[..]
}
}

Seems Like Groovy acts Differently on these two scenarios?

I have two domain classes like this, first namely Manager :
package com.mnm
class Manager {
String name;
static hasMany = [ project : Project, tasks : Tasks ]
static constraints = {
}
}
And second one namely, Project:
package com.mnm
class Project {
String projectTitle
String projectDescription
String description
static belongsTo = [ managers: Manager ]
static hasMany = [ tasks : Tasks ]
static constraints = {
}
}
And I wrote Integration test like this (to find the name of the projects via using Manager) :
void testCountProject() {
def manager = new Manager(name:'Anto').save()
manager.addToProject(new Project(projectTitle:'Grails'))
manager.addToProject(new Project(projectTitle:'Griffon'))
def noOfProjects = Manager.get(manager.id)
def found = noOfProjects.project.collect { it.projectTitle }
assertEquals(['Grails','Griffon'], found.sort())
}
Well there is no error in it and the test passes! But when I add more stuffs into to the same test like (now I'm trying the reverse, finding the Manager name via using Project) :
void testCountProject() {
def manager = new Manager(name:'Anto').save()
def project1 = new Project(projectTitle:'Grails').save()
manager.addToProject(project1)
manager.addToProject(new Project(projectTitle:'Griffon'))
def noOfProjects = Manager.get(manager.id)
def found = noOfProjects.project.collect { it.projectTitle }
assertEquals(['Grails','Griffon'], found.sort())
def noOfManager = Project.get(project.id)
def foundManager = noOfManager.managers.collect { it.name }
assertEquals(['Anto'],foundManager)
}
Now I get the error like this :
No signature of method: com.mnm.Manager.addToProject() is applicable for argument types: (null) values: [null] Possible solutions: addToProject(java.lang.Object), getProject()
Where I went wrong?
Thanks in advance.
You have the same problem in both cases, but the first isn't a proper test so it seems to work. The issue is that all properties are not-null by default, so your Project instances fail validation when you only set projectTitle.
In the first test you don't re-load the manager instance, you're still using the one in-memory because get() uses the Hibernate session as a 1st-level cache. If you flush and clear the session to force it to go to the database it will fail:
class MyTest extends GroovyTestCase {
def sessionFactory
void testCountProject() {
def manager = new Manager(name:'Anto')
manager.addToProject(new Project(projectTitle:'Grails'))
manager.addToProject(new Project(projectTitle:'Griffon'))
manager.save(flush: true)
sessionFactory.currentSession.clear()
def noOfProjects = Manager.get(manager.id)
def found = noOfProjects.project.collect { it.projectTitle }
assertEquals(['Grails','Griffon'], found.sort())
}
}
The second one fails because you call save() on the Project instance and it returns null when validation fails. You don't need to save Project instances because they will be transitively saved when the containing Manager gets saved - the more standard pattern is the one you use in the first test.
You have a few options. One is to fix the validation errors :) Another is to check for validation errors. This requires a separate save() call so you have access to the not-null instance:
def project1 = new Project(projectTitle:'Grails')
project1.save()
if (project1.hasErrors()) {
// handle errors
}
else {
manager.addToProject(project1)
}
The third is failOnError which will throw an exception when validation fails:
def project1 = new Project(projectTitle:'Grails').save(failOnError: true)
manager.addToProject(project1)