Using call read within a js function - karate

I have a slight problem I want to solve. I have the following lines of code which create 2 users which works. However the issue is that, it creates both users with the same Id from the the first line of code:
def myId = call read('classpath:karate/helpers/guid.js')
def users = function(){ karate.call('classpath:v1/api_CreateUser.feature')}
def usersResult = karate.repeat(2, users )
So I want to be able to create multiple users with different Ids. I tried the following:
* def users =
"""
function(){
var myId = null;
if(myId == null)
{
myId = call read('classpath:karate/helpers/guid.js')
}
karate.call('classpath:v1/api_CreateUser.feature');
}
"""
def usersResult = karate.repeat(2, users )
So the idea is to reset the 'myId' variable everytime to null, check if null which will be true, then call the js function which generates the id and assign the result to 'myId' variable.
Then the variable will be used on the karate.call('classpath:v1/api_CreateUser.feature') line.
Unfortunately I'm getting javascript evaluation failed error.
Anyone could help?
Thanks

Be clear about the slight difference when you are in Karate and when you are in JS. For example:
myId = call read('classpath:karate/helpers/guid.js')
This won't work. What you are looking for is:
myId = karate.call('classpath:karate/helpers/guid.js');
I recommend you read and understand the section on Karate Expressions it will save you a lot of trouble later.
When you use a JS function (that works) you should never need to worry about what you are. Just invoke it wherever, and it will "dispense" a new value. For example if you have:
* def time = function(){ return java.lang.System.currentTimeMillis() + '' }
* def first = time()
* def second = time()
Here first and second will always be different. I think now you have all you need. You are trying to access a JS variable defined in Karate from within a function, this depends on when either was initialized and I don't recommend it if you don't know what you are doing. But if you want to access the latest value of a Karate variable, the right way is to use karate.get(varNameAsString).

Related

How to make antispam function discord.py?

I need antispam function on my discord server. Please help me. I tried this:
import datetime
import time
time_window_milliseconds = 5000
max_msg_per_window = 5
author_msg_times = {}
#client.event
async def on_ready():
print('logged in as {0.user}'.format(client))
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.playing,name="stack overflow"))
#client.event
async def on_message(message):
global author_msg_counts
ctx = await client.get_context(message)
author_id = ctx.author.id
# Get current epoch time in milliseconds
curr_time = datetime.datetime.now().timestamp() * 1000
# Make empty list for author id, if it does not exist
if not author_msg_times.get(author_id, False):
author_msg_times[author_id] = []
# Append the time of this message to the users list of message times
author_msg_times[author_id].append(curr_time)
# Find the beginning of our time window.
expr_time = curr_time - time_window_milliseconds
# Find message times which occurred before the start of our window
expired_msgs = [
msg_time for msg_time in author_msg_times[author_id]
if msg_time < expr_time
]
# Remove all the expired messages times from our list
for msg_time in expired_msgs:
author_msg_times[author_id].remove(msg_time)
# ^ note: we probably need to use a mutex here. Multiple threads
# might be trying to update this at the same time. Not sure though.
if len(author_msg_times[author_id]) > max_msg_per_window:
await ctx.send("Stop Spamming")
ping()
client.run(os.getenv('token'))
And it doesn't seem to work when I type the same message over and over again. Can you guys please help me? I need the good antispam function which will work inside on_message
I think the best thing you can do is to make an event on_member_join, which will be called every time user joins. Then in this event, you can make a list instead of variables that will save user id, and their current currency.
users_currency = ["user's id", "5$", "another user's id", "7$"] and so on. Next, I would recommend saving it to a text file.
Example code
global users_currency
users_currrency = []
#client.event
global users_currency
async def on_member_join(member): #on_member_join event
user = str(member.id) #gets user's id and changes it to string
users_currency.append(user) #adds user's id to your list
users_currency.append("0") #sets the currency to 0
Now if someone will join their id will appear in list and change their currency to 0.
How can you use assigned values in list
If you keep the code close to example higher then on users_currrency[0], users_currrency[2], [...]. You will get users' ids and on users_currrency[1], users_currrency[3], etc. You will get their currency. Then you can use on_message event or #client.command to make command that will look for user's id in list and change next value - their currency.
Saving it to a text file
You have to save it in a text file (Writing a list to a file with Python) and then make a function that will run at the start of the bot and read everything from the file and assign it inside your list.
Example code:
with open("users_currency.txt") as f:
rd=f.read()
changed_to_a_list=rd.split()
users_currency = changed_to_a_list

Python Dynamic Test Plan generation

I am using Sphinx for documentation and pytest for testing.
I need to generate a test plan but I really don't want to generate it by hand.
It occurred to me that a neat solution would be to actually embed test metadata in the tests' themselves, within their respective docstrings. This metadata would include things like % complete, time remaining etc. I could then run through all of the tests (which would at this point include mostly placeholders) and generate a test plan from them. This would then guarantee that the test plan and the tests themselves would be in sync.
I was thinking of making either a pytest plugin or a sphinx plugin to handle this.
Using pytest, the closest hook I can see looks like pytest_collection_modifyitems which gets called after all of the tests are collected.
Alternatively, I was thinking of using Sphinx and perhaps copying/modifying the todolist plugin as it seems like the closest match to this idea. The output of this would be more useful as the output would slot nicely in to the existing Sphinx based docs I have though there is a lot going on in this plugin and I don't really have the time to invest in understanding it.
The docstrings could have something like this within it:
:plan_complete: 50 #% indicator of how complete this test is
:plan_remaining: 2 #the number of hours estimated to complete this test
:plan_focus: something #what is the test focused on testing
The idea is to then generate a simple markdown/rst or similar table based on the function's name, docstring and embedded plan info and use that as the test plan.
Does something like this already exist?
In the end I went with a pytest based plugin as it was just so much simpler to code.
If anyone else is interested, below is the plugin:
"""Module to generate a test plan table based upon metadata extracted from test
docstrings. The test description is extracted from the first sentence or up to
the first blank line. The data which is extracted from the docstrings are of the
format:
:test_remaining: 10 #number of hours remaining for this test to be complete. If
not present, assumed to be 0
:test_complete: #the percentage of the test that is complete. If not
present, assumed to be 100
:test_focus: The item the test is focusing on such as a DLL call.
"""
import pytest
import re
from functools import partial
from operator import itemgetter
from pathlib import Path
whitespace_re = re.compile(r'\s+')
cut_whitespace = partial(whitespace_re.sub, ' ')
plan_re = re.compile(r':plan_(\w+?):')
plan_handlers = {
'remaining': lambda x:int(x.split('#')[0]),
'complete': lambda x:int(x.strip().split('#')[0]),
'focus': lambda x:x.strip().split('#')[0]
}
csv_template = """.. csv-table:: Test Plan
:header: "Name", "Focus", "% Complete", "Hours remaining", "description", "path"
:widths: 20, 20, 10, 10, 60, 100
{tests}
Overall hours remaining: {hours_remaining:.2f}
Overall % complete: {complete:.2f}
"""
class GeneratePlan:
def __init__(self, output_file=Path('test_plan.rst')):
self.output_file = output_file
def pytest_collection_modifyitems(self, session, config, items):
#breakpoint()
items_to_parse = {i.nodeid.split('[')[0]:i for i in self.item_filter(items)}
#parsed = map(parse_item, items_to_parse.items())
parsed = [self.parse_item(n,i) for (n,i) in items_to_parse.items()]
complete, hours_remaining = self.get_summary_data(parsed)
self.output_file.write_text(csv_template.format(
tests = '\n'.join(self.generate_rst_table(parsed)),
complete=complete,
hours_remaining=hours_remaining))
def item_filter(self, items):
return items #override me
def get_summary_data(self, parsed):
completes = [p['complete'] for p in parsed]
overall_complete = sum(completes)/len(completes)
overall_hours_remaining = sum(p['remaining'] for p in parsed)
return overall_complete, overall_hours_remaining
def generate_rst_table(self, items):
"Use CSV type for simplicity"
sorted_items = sorted(items, key=lambda x:x['name'])
quoter = lambda x:'"{}"'.format(x)
getter = itemgetter(*'name focus complete remaining description path'.split())
for item in sorted_items:
yield 3*' ' + ', '.join(map(quoter, getter(item)))
def parse_item(self, path, item):
"Process a pytest provided item"
data = {
'name': item.name.split('[')[0],
'path': path.split('::')[0],
'description': '',
'remaining': 0,
'complete': 100,
'focus': ''
}
doc = item.function.__doc__
if doc:
desc = self.extract_description(doc)
data['description'] = desc
plan_info = self.extract_info(doc)
data.update(plan_info)
return data
def extract_description(self, doc):
first_sentence = doc.split('\n\n')[0].replace('\n',' ')
return cut_whitespace(first_sentence)
def extract_info(self, doc):
plan_info = {}
for sub_str in doc.split('\n\n'):
cleaned = cut_whitespace(sub_str.replace('\n', ' '))
splitted = plan_re.split(cleaned)
if len(splitted) > 1:
i = iter(splitted[1:]) #splitter starts at index 1
while True:
try:
key = next(i)
val = next(i)
except StopIteration:
break
assert key
if key in plan_handlers:
plan_info[key] = plan_handlers[key](val)
return plan_info
From my conftest.py file, I have a command line argument configured within a pytest_addoption function: parser.addoption('--generate_test_plan', action='store_true', default=False, help="Generate test plan")
And I then configure the plugin within this function:
def pytest_configure(config):
output_test_plan_file = Path('docs/source/test_plan.rst')
class CustomPlan(GeneratePlan):
def item_filter(self, items):
return (i for i in items if 'tests/hw_regression_tests' in i.nodeid)
if config.getoption('generate_test_plan'):
config.pluginmanager.register(CustomPlan(output_file=output_test_plan_file))
#config.pluginmanager.register(GeneratePlan())
Finally, in one of my sphinx documentation source files I then just include the output rst file:
Autogenerated test_plan
=======================
The below test_data is extracted from the individual tests in the suite.
.. include:: test_plan.rst
We have done something similar in our company by using Sphinx-needs and Sphinx-Test-Reports.
Inside a test file we use the docstring to store our test-case incl meta-data:
def my_test():
"""
.. test:: My test case
:id: TEST_001
:status: in progress
:author: me
This test case checks for **awesome** stuff.
"""
a = 2
b = 5
# ToDo: chek if a+b = 7
Then we document the test cases by using autodoc.
My tests
========
.. automodule:: test.my_tests:
:members:
This results in some nice test-case objects in sphinx, which we can filter, link and present in table and flowcharts. See Sphinx-Needs.
With Sphinx-Test-Reports we are loading the results into the docs as well:
.. test-report: My Test report
:id: REPORT_1
:file: ../pytest_junit_results.xml
:links: [[tr_link('case_name', 'signature')]]
This will create objects for each test case, which we also can filter and link.
Thanks of tr_link the result objects get automatically linked to the test case objects.
After that we have all needed information in sphinx and can use e.g. .. needtable:: to get custom views on it.

Variable inside a variable in Jenkinsfile

What is the correct Jenkinsfile syntax in order to use a variable value when executing command with another variable?
For example:
def lastItemIndex = "${json.items.size()-1}"
def path = "${json.items[${lastItemIndex}].assets.downloadUrl}"
echo "${path}"
First variable is lastItemIndex and second one is json.
The second row is not running properly when I tried different options.
The syntax in your second row is mostly fine. Your problem is that you are storing the return of lastItemIndex as a String and then attempting to use it as an Integer in your second row of code.
You can fix your first row with:
lastItemIndex = json.items.size() - 1
and then it will be an Integer type and def path = "${json.items[lastItemIndex].assets.downloadUrl}" will succeed.
Alternatively, you could just have the second line of code with:
def path = "${json.items[-1].assets.downloadUrl}"
to access the last element of the array.
Note that in general if you need to convert a String to an Integer within a Jenkins Pipeline via Groovy you can utilize the to_Integer method.
Thanks to Matt, eventually that what works for me:
def lastItemIndex = json.items.size()-1
def path = json.items[lastItemIndex].assets.downloadUrl

Default values for query parameters

Please forgive me if my question does not make sense.
What im trying to do is to inject in values for query parameters
GET1 File
Scenario:
Given path 'search'
And param filter[id] = id (default value or variable from another feature file)
POST1 File
Scenario:
def newid = new id made by a post call
def checkid = read call(GET1) {id : newid}
like if one of my feature files creates a new id then i want to do a get call with the above scenario. therefore i need a parameter there which takes in the new id.
On the other hand if i do not have an id newly created or the test creating it is not part of the suite. i want to still be able to run the above mentioned scenario but this time it has a default value to it.
Instead of param use params. It is designed so that any keys with null values are ignored.
After the null is set on the first line below, you can make a call to another feature, and overwrite the value of criteria. If it still is null, no params will be set.
* def criteria = null
Given path 'search'
And params { filter: '#(criteria)' }
There are multiple other ways to do this, also refer to this set of examples for data-driven search params: dynamic-params.feature
The doc on conditional logic may also give you some ideas.

passing msqli to a function - can't suss out why it's not working

I've searched high and low for an answer on this, but I'm either missing something, or I just can't find anything in this context.
Background - trying to avoid spaghetti frenzy with a little casual project I'm starting; part of this will involve mainly just calls to a mysql database, displaying table content and so on. Simply put, like a CRM I guess.
I may be way off base here, but I want to be able to create my sql calls as functions, which will help if/when I tweak and tune, as well as creating a cleaner code for what I'm looking to do.
So, without further ado, I have this as a demomstration:
echo "<table>";
selectall('actions','content',$mysqli);
echo "</table><br><br>";
What this does is show all rows from my table of 'actions. "content" is just an example field name I'm passing through that I want to display, as it is the main human-relevant field name in that table. I'm also passing $mysqli through here for my function db call.
My function looks like this:
function selectall($s_table,$s_content,$mysqli){
$query = "SELECT * FROM " . $s_table;
$resource = $mysqli->query($query);
if ( !$resource ) throw new Exception($db->error);
while ( $row = $resource->fetch_assoc() ) {
$id = $row['id'];
echo "<tr><td>{$row[$s_content]}</td></tr>";
}
$resource->free();
$mysqli->close();
}
However.... it doesn't work, and it seems to throw a wobbly saying:
Warning: mysqli::query(): Couldn't fetch mysqli
This points to the action within the line $resource = $mysqli->query($query);
I know the function and everything is ok, as if I restate and declare $mysqli within the first line of the function, like so...
$mysqli = new mysqli(username password and so on in here);
... it works spot on.
$mysqli exists and works within the same code that is passing the variable within the function too.
This is early stages, so by shuffling the code around trying to poke the $mysqli pass into life I have perhaps made the code a little messier that intended, so try not to worry too much about that.
Anyone any ideas why it doesn't like this?
D'oh...
I had a
$mysqli->close();
in the lines above. Solved myself.
For reference, this is my function:
function selectall($s_table,$s_field,$mysqli){
if ($mysqli->connect_error) {die('Connect Error (' . $mysqli->connect_errno . ') '. $mysqli->connect_error);}
$s_table = preg_replace('/[^0-9a-zA-Z_]/', '', $s_table); // Cleans up the table name variable
$s_field = preg_replace('/[^0-9a-zA-Z_]/', '', $s_field); // Cleans up the field name variable
$query = "SELECT * FROM " . $s_table; // Adds passed table name to the select all
$resource = $mysqli->query($query);
if ( !$resource ) throw new Exception($db->error);
while ( $row = $resource->fetch_assoc() ) {
echo "<tr><td>{$row[$s_field]}</td></tr>"; // Content for each row of the select all
}
$resource->free();
$mysqli->close();
}
As you can see, I've also tried to protect the variables that enter the function.
This can be called via:
selectall('actions','content',$mysqli);
In this context, I want to view all the entries in the 'actions' table by the field name 'content'. This function, with some code above and below for a table, will create a new row for each entry.
I'll probably evolve a few, already created on that includes a delete button at the end of the line which is 'selectalldel'.
Open to comments on whether this actually is worthwhile, but thought I'd post up my corrected stupidity in case anyone finds this useful.