Using Spock test how to validate json attributes key-value pairs comes as dynamically inputs - unit-testing

How to write right test, in order to test the below csv data stored in database table. In the input other than item, anything could be optional.
In this, item is key, rest all goes as part of json format typically it looks like this in database {"brand": "Brand6", "category": "Category6", "subcategory": "Sub-Category6"}
Input:
item,category,subcategory,brand,type,feature
TEST-ITEM6,Category6,Sub-Category6,Brand6
TEST-ITEM7,Category7,Sub-Category7,Brand7,TYPE7,FEATURE7
TEST-ITEM8,Category8,Sub-Category8,Brand8,TYPE8,FEATURE8
Test case tried:
def "Case 3a. Verify New 2 records with two more additional fields along with earlier fields to the same tenant"() {
expect:
sql().eachRow("SELECT * FROM item WHERE item IN ('"+item+"')") { row ->
def dbItem = row[0]
def dbAttributes = getJsonToObject(row[1])
def dbCategory = dbAttributes.getAt("category").toString()
def dbSubCategory = dbAttributes.getAt("subcategory").toString()
def dbBrand = dbAttributes.getAt("brand").toString()
def dbType = dbAttributes.getAt("type").toString()
def dbFeature = dbAttributes.getAt("feature").toString()
assert dbItem == item
assert category == dbCategory
assert subcategory == dbSubCategory
assert brand == dbBrand
assert type == dbType
assert feature == dbFeature
}
where:
item << ['TEST-ITEM6', 'TEST-ITEM7', 'TEST-ITEM8']
category << ['Category6','Category7', 'Category8']
subcategory << ['Sub-Category6','Sub-Category7', 'Sub-Category8']
brand << ['Brand6','Brand7', 'Brand8']
type << ['TYPE7', 'TYPE8']
feature << ['FEATURE7', 'FEATURE8']
}
Error:
Condition not satisfied:
type == dbType
| | |
TYPE8| TYPE7
false
1 difference (80% similarity)
TYPE(8)
TYPE(7)
Expected :TYPE7
Actual :TYPE8

In this case I would recommend to use Data Tables as it becomes more readable and resembles your input more closely.
And while type and feature are optional, you need to provide some value for it. It could be null or it could be an empty List or Map (if an Item can have more than one type/feature)
So you where block might look like this:
item | category | subcategory | brand | typeFeatureMap
'TEST-ITEM6' | 'Category6' | 'Sub-Category6' | 'Brand6' | [:] // empty
'TEST-ITEM7' | 'Category7' | 'Sub-Category7' | 'Brand7' | ['TYPE7':'FEATURE7']
'TEST-ITEM8' | 'Category8' | 'Sub-Category8' | 'Brand8' | ['TYPE8':'FEATURE8']
I would also recommend to collect the data and then compare it, so you get around ordering issues.
So bofore your eachRow do something like
def itemFeatures = [:]
In your eachRow do something like
itemFeatures.put(dbAttributes.getAt("type").toString(), dbAttributes.getAt("feature").toString())
And afterwards
itemFeatures == typeFeatureMap
While not answering your question, I would recommend to think about separating the tests from your database if possible.
If you create separate tests for an database abstraction layer and your business logic, you'll be more happy in the long run ;)

For the optional fields you can use the Elvis operator ?: like this (sorry, long code, I modeled your database and two new test cases, one with many optional fields and one failing test):
package de.scrum_master.stackoverflow
import spock.lang.Specification
import spock.lang.Unroll
class DataTableWithOptionalItemsTest extends Specification {
#Unroll
def "Case 3a. Verify record '#item' with possibly optional fields"() {
expect:
testData[item].each { row ->
def dbItem = row["item"]
def dbCategory = row["category"]
def dbSubCategory = row["subcategory"]
def dbBrand = row["brand"]
def dbType = row["type"]
def dbFeature = row["feature"]
assert dbItem == item
assert (category ?: dbCategory) == dbCategory
assert (subcategory ?: dbSubCategory) == dbSubCategory
assert (brand ?: dbBrand) == dbBrand
assert (type ?: dbType) == dbType
assert (feature ?: dbFeature) == dbFeature
}
where:
item | category | subcategory | brand | type | feature
'TEST-ITEM6' | 'Category6' | 'Sub-Category6' | 'Brand6' | null | null
'TEST-ITEM7' | 'Category7' | 'Sub-Category7' | 'Brand7' | 'TYPE7' | 'FEATURE7'
'TEST-ITEM8' | 'Category8' | 'Sub-Category8' | 'Brand8' | 'TYPE8' | 'FEATURE8'
'TEST-ITEM9' | null | null | null | null | null
'TEST-FAIL' | 'CategoryX' | 'Sub-CategoryX' | 'BrandX' | 'TYPEX' | 'FEATUREX'
}
static final testData = [
'TEST-ITEM6': [
[
item : 'TEST-ITEM6',
category : 'Category6',
subcategory: 'Sub-Category6',
brand : 'Brand6',
type : 'dummy',
feature : null
],
[
item : 'TEST-ITEM6',
category : 'Category6',
subcategory: 'Sub-Category6',
brand : 'Brand6',
type : null,
feature : "foo"
]
],
'TEST-ITEM7': [
[
item : 'TEST-ITEM7',
category : 'Category7',
subcategory: 'Sub-Category7',
brand : 'Brand7',
type : 'TYPE7',
feature : 'FEATURE7'
],
[
item : 'TEST-ITEM7',
category : 'Category7',
subcategory: 'Sub-Category7',
brand : 'Brand7',
type : 'TYPE7',
feature : 'FEATURE7'
]
],
'TEST-ITEM8': [
[
item : 'TEST-ITEM8',
category : 'Category8',
subcategory: 'Sub-Category8',
brand : 'Brand8',
type : 'TYPE8',
feature : 'FEATURE8'
],
[
item : 'TEST-ITEM8',
category : 'Category8',
subcategory: 'Sub-Category8',
brand : 'Brand8',
type : 'TYPE8',
feature : 'FEATURE8'
]
],
'TEST-ITEM9': [
[
item : 'TEST-ITEM9',
category : 'Category1',
subcategory: 'Sub-Category1',
brand : 'Brand1',
type : 'TYPE1',
feature : 'FEATURE1'
],
[
item : 'TEST-ITEM9',
category : null,
subcategory: null,
brand : null,
type : null,
feature : null
]
],
'TEST-FAIL' : [
[
item : 'TEST-FAIL',
category : 'CategoryX',
subcategory: 'Sub-CategoryX',
brand : 'BrandY',
type : 'TYPEX',
feature : 'FEATUREX'
]
]
]
}

Related

Django JSONField nested greater than operation

Here's the structure of the JSONField - data I have.
data : {
"animals" : [
"animal_id" : 2321,
"legs" : [
{
"name" : "front",
"measured_at" : 1596740795.453353
},
{
"name" : "back",
"measured_at": 1596740795.453353
}
]
]
}
I need to find all records that match the following condition
legs-name = "front" OR
measured_at is greater than 6 hrs from now.
I have tried the following
six_hrs_ago = (datetime.utcnow() - timedelta(hours = 6)).timestamp()
obj = Model.objects.filter(data__has_key='animals').
filter(Q(data__animals__legs__name="front") | Q(data__animals__legs__measured_at__gt = six_hrs_ago))))
This doesn't work, and it fetches no record.
I also tried to just filter the records with legs - name - front, that doesn't work either
obj = Model.objects.filter(data__animals__contains=[{'legs': [{"name": "front"}]}])
Any ideas are appreciated.

ELM : Onlick button to port the particular string in HTML message to JS when i click the button

I have model
type alias Model =
{
url : String
, devices : List Device
, input : String
, isFilter : Bool
, deviceSuggestion : Maybe Device
}
type alias Device =
{
status : String
, license_plate : String
, device_id : String
}
and below is my render view and i want to get the device_id and port to JS
renderPost : Device -> Html Msg
renderPost devices =
div []
[
[ text "Device_id : "
, button [ onClick PortDeviceID ] [ text (Debug.toString devices.device_id) ]
, pre [] [ text "device_model : " , text (Debug.toString devices.device_model) ]
, pre [] [ text "license_plate : " , text (Debug.toString devices.license_plate) ]
]
renderPosts : Model -> Html Msg
renderPosts model =
([ text "Device List" ] ++ List.map (\device -> renderPost device) model.devices)
and my updates is here. I have no idea how to get the text because the devices.device_id is from json file
PortDeviceID ->
( { model | deviceID = "" }, //port device_id --> not working)
GotResult result ->
case result of
Ok devices ->
( { model | devices = devices }, portDevice devices ) // this give full list
I want to do like , when i click the button then i can port the specific device_id to JS. The thing is it is in Device -> Html Msg format , so im stuck lol. Any ideas to do it? Any help is appreciate !
Case solved here,
in View , i do like below, instantiate the devices.device_id
renderPost : Device -> Html Msg
renderPost devices =
div []
[
[ text "Device_id : "
, button [ onClick (PortDeviceID devices.device_id)] [ text (Debug.toString devices.device_id) ]
Then in updates , same let the portDeviceID String
PortDeviceID deviceID->
( { model | deviceID = "" }, portDeviceID deviceID )
Anyway thanks for help Jack Leow

AppSync + DynamoDB: Query with filter doesn't return all valid items

I have Appsync API connecting to a Dynamo table.
Dynamo table has data : ("id" is key and "year" is sort key)
|--------------|-------------|-------------|-------------|-------------|
| id | year | name | class | subject |
|--------------|-------------|-------------|-------------|-------------|
| 001 | 2017 | Tom | E1 | Math |
|--------------|-------------|-------------|-------------|-------------|
| 002 | 2017 | Mary | E1 | Math |
|--------------|-------------|-------------|-------------|-------------|
| 003 | 2017 | Peter | E1 | Math |
|--------------|-------------|-------------|-------------|-------------|
the schema
type Query {
listStudents(filter: TableStudentFilterInput, limit: Int, nextToken: String): StudentConnection
}
type StudentConnection {
items: [Student]
nextToken: String
}
input TableStudentFilterInput {
id: TableStringFilterInput
year: TableStringFilterInput
name: TableStringFilterInput
class: TableStringFilterInput
subject: TableStringFilterInput
}
type Student {
id: String!
year: String!
name: String
class: String
subject: String
}
Query:
query listStudentByYear {
listStudents (filter:{year:{eq:"2017"}}) {
items {
id
year
name
class
subject
}
}
}
The issue: The query return 001 and 002, but not 003.
When I tried to update "id" from 003 to 004, then the query returns correctly 001, 002, 004.
This weird issue happens quite frequently, after some times, the AppSync query returns an incomplete result (missing some).
Any suggestion is appreciated.
Check out this thread from amplify-js issues.
Essentially what is happening is a limit is applied before the filter. So if you have a limit of 20 and 003 is entry number 21 it will not be included in the filter operation.
A workaround here is to remove the limit from you resolver in the AWS AppSync Console
So change this:
#set( $ListRequest = {
"version": "2017-02-28",
"limit": $limit
})
to this:
#set( $ListRequest = {
"version": "2017-02-28",
} )
Now this isn't a graceful workaround as the DynamoDB Scan will only return 1MB of data meaning this solution will not work for large (practical) implementations.

How to annotate field without duplicate fields django

So I basically have this simple model:
class BaseLesson(models.Model):
YOUTUBE_VIDEO = '0'
MARKDOWN = '1'
TYPE_CHOICES = (
(YOUTUBE_VIDEO, 'youtube-video'),
(MARKDOWN, 'markdown'),
)
type = models.CharField(
max_length=10, choices=TYPE_CHOICES, default=MARKDOWN, verbose_name=_('type'))
shown_users = models.ManyToManyField(
User, related_name='lessons', verbose_name=_('shown users'), blank=True)
objects = managers.BaseLessonManager()
There is a many-to-many relationship with the User model in shown_users
And I wanna annotate the is_shown status based on the many-to-many table, so I did this:
class BaseLessonManager(InheritanceManager, CachingManager):
def get_lesson_with_is_shown(self, user):
shown_user_case = django_models.Case(
django_models.When(shown_users__id=user.id,
then=django_models.Value(True)),
default=django_models.Value(False),
output_field=django_models.BooleanField())
return self.get_queryset().annotate(
is_shown=shown_user_case)
The problem with this is that if user1 and user2 saw the same lesson it will be duplicate, for example:
+-----------------+-----------+
| lesson_id | user_id |
+-----------------+-----------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+-----------------+-----------+
For such case, I will get these duplicated lessons:
{
"id": 1
"type": "0",
"is_shown": true
},
{
"id": 1
"type": "0",
"is_shown": false
},
{
"id": 1
"type": "0",
"is_shown": false
}
So it's checking each related lesson field in the SHOWN_USERS table... sample photo:
https://imgur.com/GJCPWjk
What I tried so far:
1. Exclude:
I added an exclude expression to get rid of the extra lessons:
return self.get_queryset().annotate(
is_shown=shown_user_case).exclude(
django_models.Q(is_shown=False) & django_models.Q(shown_users__id=user.id))
And I think this is super ugly cuz if I have 1000 users and 50 lessons it means I'm taking all 50000 fields then filter 50 of them :(
Is there any cleaner way to do that ?
2. Distinct:
I have tried distinct before and it's not fixing the problem, instead of shown the lesson three times it will show:
once (is_shown = True),
and another time (is_shown = False)
I managed to fix the problem, and If figured out that my way of querying many-to-many is the problem, instead of shown_users__id == user.id I used id__in==user.lessons.values('id'), full code:
class BaseLessonManager(InheritanceManager, CachingManager):
def with_is_shown(self, user):
user_shown_lessons = user.lessons.values('id')
shown_user_case = django_models.Case(
django_models.When(id__in=user_shown_lessons,
then=django_models.Value(True)),
default=django_models.Value(False),
output_field=django_models.BooleanField())
return self.get_queryset().annotate(
is_shown=shown_user_case)

SPOCK - All #Shared Variables are NULL

Here is my test class:
import grails.test.mixin.*
import spock.lang.Specification
import spock.lang.Unroll
import spock.lang.*
import groovy.util.logging.Slf4j
#Slf4j
#Mock([PromoRule, PromoCode, SecUser])
#TestFor(PromoService)
class PromoServiceSpec extends Specification {
#Shared testUser
#Shared testCode
#Shared testRule
def setup() {
}
#Unroll
def 'valid promo codes - #user, #code'() {
given:
testRule = new PromoRule(
name : "ZTEST",
receiverAmount : 5,
receiverAmountType : PromoRule.AmountType.DOLLARS,
senderAmount : 0,
senderAmountType : PromoRule.AmountType.DOLLARS,
receiverPointsAmount : null,
receiverPointsAmountType : null,
receiverMaxUse : null,
)
testRule.save(flush:true, failOnError:true)
testUser = new SecUser(
id: 1,
version: 0,
accountExpired: false,
accountLocked: false,
age: 9000,
balance: 100,
dateCreated: new Date(),
emailVerified: true,
enabled: true,
firstName: 'Sir',
lastName: 'Buttocks',
lastUpdated: new Date(),
lockedBalance: 0,
username: "1",
staff: false,
displayName: 'sir_buttocks',
usernameChosen: true,
depositMade: true,
depositOfferRecentlySeen: false,
pin: null
)
testUser.save(flush: true, failOnError: true)
testCode = new PromoCode(
rule : testRule,
code : "3",
senderId : 1,
)
testCode.save(flush:true, failOnError:true)
expect:
service.isValidPromoCode(user, code) == value
where:
user | code || value
testUser | testCode || true
}
}
When I run this test, I get the following:
| Failure: valid promo codes - null, null(skillz.PromoServiceSpec)
| Condition not satisfied:
service.isValidPromoCode(user, code) == value
| | | | | |
| false null null | true
skillz.PromoService#20e0e9d5 false
I have tried a ton of different configurations and layouts, all of them either getting me a null pointer exception (to the variable itself) or a null value for the variable.
Doing all the definitions at static variables didn't change anything either, same result as using #Shared.
I've tried mocking these as well, but I always get null exceptions when trying to execute .Mock() for the class...
Thanks!!
I'm not exactly sure what you are trying to achieve here, but the where block is evaluated before (the rest of) the method is first entered, and at that time, your shared variables are null. You'd have to set them earlier, e.g. in a setupSpec (not setup) method.